CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Page 3 of 3 FirstFirst 123
Results 31 to 40 of 40
  1. #31
    2kaud's Avatar
    2kaud is offline Super Moderator Power Poster
    Join Date
    Dec 2012
    Location
    England
    Posts
    7,824

    Re: adding rational number using only class functions

    Quote Originally Posted by D_Drmmr View Post
    I would strongly advice against mixing signed and unsigned integers. Just take this simple example:
    Code:
    int numerator = -1;
    unsigned int denominator = 2;
    bool isNegative = (numerator * denominator < 0);
    What do you think the value of isNegative is?
    Due to type promotion the result of the multiplication is an unsigned int, which cannot be negative, so isNegative is false.

    Arithmetics with unsigned values is too prone to errors like this. You are better off just using an int, even if it should never take negative values. Only use unsigned integers when you want to use individual bits as flags.
    Yeah, you shouldn't mix signed and unsigned.
    All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!

    C++23 Compiler: Microsoft VS2022 (17.6.5)

  2. #32
    Join Date
    Apr 2000
    Location
    Belgium (Europe)
    Posts
    4,626

    Re: adding rational number using only class functions

    Quote Originally Posted by 2kaud View Post
    Yeah, you shouldn't mix signed and unsigned.
    Well... You can, just be aware of what's going to happen.
    This isn't much different from many of the other types of calculations, like mixing integer and floating point.

    I found the advice to use unsigneds only for individual bits a bit (pun intended) weird. And potentially even dangerous advice.

    There is a reason to use signed types, and there are reasons to use unsigned types, and there are reasons to use floating point types.

    If you can, it's Always better to stick with a single type and do all your calculations with that single type, but unfortunately life isn't Always that easy. You can mix them together, but in that case, you should be aware of the behaviour.
    I see even a lot of devs getting this wrong when they're faced with "unusual" platforms where signed int aren't using 2's complement, or where the amount of bits in a type is considerably different from what they're used to.
    And floating point of course are just downright "evil" when it comes to expectancy of accuracy (note FP types have high precision but are inaccurate), and sometimes under the right circumstances, you do actually get guaranteed accuracy as well, which confuses most that just gotten used to accepting the fact of the contrary. :-D

  3. #33
    Join Date
    Jul 2005
    Location
    Netherlands
    Posts
    2,042

    Re: adding rational number using only class functions

    Quote Originally Posted by OReubens View Post
    I found the advice to use unsigneds only for individual bits a bit (pun intended) weird. And potentially even dangerous advice.

    There is a reason to use signed types, and there are reasons to use unsigned types, and there are reasons to use floating point types.
    Obviously. My point is that "the value should never be negative" is not a reason to use an unsigned type. There may still be situations where you need to take a difference or use the value in an arithmetic operation involving a number that can be negative. I've tried using unsigned ints for indices for a while and stopped doing it, because it's simply too prone to errors.
    Cheers, D Drmmr

    Please put [code][/code] tags around your code to preserve indentation and make it more readable.

    As long as man ascribes to himself what is merely a posibility, he will not work for the attainment of it. - P. D. Ouspensky

  4. #34
    Join Date
    Apr 2000
    Location
    Belgium (Europe)
    Posts
    4,626

    Re: adding rational number using only class functions

    because indexes that could become negative aren't going to cause errors ?

    Actually... "the value should never be negative" is a very good reason to make it an unsigned type.
    What people often fail to register in that is that we're not talking ONLY about end results, but also all intermediate results that may or may not even make it to memory but are entirely kept in CPU registers.

    Personally I advocate the other way around. Use unsigned for everything unless a value HAS to allow negatives. And be careful when you do have to mix those few signeds with your unsigneds.
    it's signed types that are the bigger evil here. (mainly because of the undefined overflow).


    ideally you want to make indices (and as many integer types as possible) unsigned, even if only for the fact that for all CPU's I'm aware off the unsigned operations are at least as fast as the signed variants, but on many platforms (including x86) they tend to be faster.
    - divisions by const powers of 2 can be done with a shift
    - divisions by some const non-powers of 2 can be done by multiplication of the inverse
    - division by non-const can be optimized in the CPU for unsigned.
    - conversions from signed to larger signed types can be slower because of the need to sign extend
    - conversions from signed to smaller signed types is undefined
    - modulo operations on signeds are going to bit you in the *** sooner or later, as murphy's law dictates, it'll happen in the most horrible way at the most horrible time possible.

    Also unsigned overflow is very clearly defined and thus gives expected results, whereas signed overflow in C/C++ is undefined (specific behaviour may be defined for your specific compiler ofc, but it's not going to be portable).

  5. #35
    2kaud's Avatar
    2kaud is offline Super Moderator Power Poster
    Join Date
    Dec 2012
    Location
    England
    Posts
    7,824

    Re: adding rational number using only class functions

    Personally I advocate the other way around. Use unsigned for everything unless a value HAS to allow negatives. And be careful when you do have to mix those few signeds with your unsigneds.
    That has always been my take on this (though I was starting to really question this after reading previous posts) and what I was trying to advocate (clumsily) way back in post #4 and since - and yes, mixing signed and unsigned can bite!
    All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!

    C++23 Compiler: Microsoft VS2022 (17.6.5)

  6. #36
    Join Date
    Jul 2005
    Location
    Netherlands
    Posts
    2,042

    Re: adding rational number using only class functions

    Quote Originally Posted by OReubens View Post
    because indexes that could become negative aren't going to cause errors ?
    No more with a signed integer than with an unsigned. What's your point?
    Quote Originally Posted by OReubens View Post
    Actually... "the value should never be negative" is a very good reason to make it an unsigned type.
    What people often fail to register in that is that we're not talking ONLY about end results, but also all intermediate results that may or may not even make it to memory but are entirely kept in CPU registers.
    Like I said, I tried this for a while and stopped, because I realized I was over-complicating my code.
    With an unsigned integer, code like this may fail to do what you want.
    Code:
    void drawPolygon(const std::vector<Point>& points)
    {
        for (size_t i = 0, n = points.size(); i < n - 1; ++i) {
            drawLine(points[i], points[i + 1]);
        }
    }
    When points is empty, this code will access out of range elements of the vector. So you have to write "i + 1 < n" instead of "i < n - 1". The fact that these two expressions don't yield the same result is so counter-intuitive, that it's enough reason for me to prefer using signed integers.
    Quote Originally Posted by OReubens View Post
    ideally you want to make indices (and as many integer types as possible) unsigned, even if only for the fact that for all CPU's I'm aware off the unsigned operations are at least as fast as the signed variants, but on many platforms (including x86) they tend to be faster.
    I highly doubt you will see a significant difference in performance with any real-world code on a common platform by simply switching from signed to unsigned. Can you back up your claim with some timings?
    Quote Originally Posted by OReubens View Post
    - conversions from signed to smaller signed types is undefined
    Never heard of that. Can you point where in the standard this is stated?

    By the way, in code where you are not bit-shifting, integer overflow is hardly ever something you want to happen in your program. Therefore, the fact that signed integer overflow is undefined by the standard is not a reason to avoid signed integers in general.
    Cheers, D Drmmr

    Please put [code][/code] tags around your code to preserve indentation and make it more readable.

    As long as man ascribes to himself what is merely a posibility, he will not work for the attainment of it. - P. D. Ouspensky

  7. #37
    Join Date
    Oct 2008
    Posts
    1,456

    Re: adding rational number using only class functions

    Quote Originally Posted by D_Drmmr View Post
    When points is empty, this code will access out of range elements of the vector. So you have to write "i + 1 < n" instead of "i < n - 1". The fact that these two expressions don't yield the same result is so counter-intuitive, that it's enough reason for me to prefer using signed integers.
    that code is not a good example because it's counterintuitive with or without unsigneds; a reviewer seeing that loop will always end up asking himself "what happens when points.size() equals 0 or is odd" ? so writing "i + 1 < n" instead of "i < n - 1" would not be a good enough solution anyway; IMO it's better to be explicit by adding an if() before the loop and leave the for() condition as it is ( or, if that happens to be too costly, just leave that as a precondition and let UB rule ).

    IMHO, well written code has more or less the same chance of having arithemtic related bugs, with or without signeds. I agree with OReubens, in c-like languages one always needs to think carefully when doing arithmetic ( being it signed/unsigned/mixed/floating/whatever ).

    Moreover, what happens if your signed type has a smaller upper limit than size_t ? yes, this may not be an issue with "real" containers and a properly choosen signed type, but, say, consider generic code that may need to work with lazy sequences too that need not be in the real process address space, or algorithms working on offset indeces etc ...

    at best, that example shows that indices are dangerous by themselves, and should be replaced by better alternatives like ranges or iterators ...
    Last edited by superbonzo; February 28th, 2015 at 01:18 PM. Reason: typos

  8. #38
    2kaud's Avatar
    2kaud is offline Super Moderator Power Poster
    Join Date
    Dec 2012
    Location
    England
    Posts
    7,824

    Re: adding rational number using only class functions

    With an unsigned integer, code like this may fail to do what you want.
    Code:
    void drawPolygon(const std::vector<Point>& points)
    {
        for (size_t i = 0, n = points.size(); i < n - 1; ++i) {
            drawLine(points[i], points[i + 1]);
        }
    }
    When points is empty, this code will access out of range elements of the vector. So you have to write "i + 1 < n" instead of "i < n - 1".
    Yes, as the code is written it could be an issue, but consider
    Code:
    void drawPolygon(const std::vector<Point>& points)
    {
        for (size_t i = 1, n = points.size(); i < n; ++i) {
            drawLine(points[i - 1], points[i]);
        }
    }
    All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!

    C++23 Compiler: Microsoft VS2022 (17.6.5)

  9. #39
    Join Date
    Apr 2000
    Location
    Belgium (Europe)
    Posts
    4,626

    Re: adding rational number using only class functions

    Quote Originally Posted by D_Drmmr View Post
    No more with a signed integer than with an unsigned. What's your point?
    My point being. unsigneds or signeds aren't goign to save your live. So advocating "use signeds for indexes because" is just going to happen for unsigned as well.


    Like I said, I tried this for a while and stopped, because I realized I was over-complicating my code.
    see next...


    I highly doubt you will see a significant difference in performance with any real-world code on a common platform by simply switching from signed to unsigned. Can you back up your claim with some timings?
    That would depend on your platform. On a "mammoth" CPU like the x86 with many instructions, several of which are similar/overlapping and which sells in high enough volumes that the engineers developing the chip have had plenty of time and resources to make the chip as optimal as possible...
    No, not significantly.

    Do note that even on todays x86, the MUL is slightly faster than the IMUL and DIV is slightly faster than IDIV.

    On a processor that doesn't have 2 variants of an integer MUL / DIV, the norm is to convert to unsigned, multiply/divide and correct for signs. That's goign to be costly.
    On a processor that doesn't even have MUL/DIV (these chips are sold in the billions btw, they're in everything from your camera, to your washing machine, your toaster, ...) and where those are done with more low level instructions, the difference is goign to be even bigger.
    Now, you could argue, "wait, you just argued for selling in the millions for the x86 that this makes the whipe more optimised", true, but on a x86 pricing is less of an issue, on a low end chip there simply is no real incentive to spend money on making the chips more faster by making them more complex, every transistor costs money and there's just no wiggleroom on those chips.

    Never heard of that. Can you point where in the standard this is stated?
    Maybe a bit hasty on my part. it's "implementation defined" (for signeds) which makes it "undefined unless you know in advance what you're coding for", if you write portable code, signed integer narrowing may yield different results on different compilers (even if each compiler defines it) and thus code that has worked for many years on a particular platform suddenly stops working on another platform. Now consider the effort you may need if you have to #define every single signed integer narrowing to get the results you expect.
    Unsigneds do not show this behaviour, narrowing is the same regardless of the platform.

    What happens if you have an unsigned int holding 256 and you assign this to an unsigned char (assuming char is 8 bit)... the answer is 0 (lowest 8 bits), Always.
    what happens if you have a signed int holding -256 and you assign this to an signed char... Try taking a guess... do you even know for YOUR plarform without looking it up and without taking a calculator or sheet of paper ? Are you even sure it's going to be the same for all compiler brands on your platform ?

    By the way, in code where you are not bit-shifting, integer overflow is hardly ever something you want to happen in your program. Therefore, the fact that signed integer overflow is undefined by the standard is not a reason to avoid signed integers in general.
    I never claimed so. You should Always write your code in such a way it is resilient for overflow cases. (again, see next post)
    but you can't always avoid it, but it's easy to overlook that overflow may be happening in one of the intermediaries in a more complex calculation. You may not even noticed the overflow or have written "around" it somehow. and then you port to another platform where the signed overflow happens to result in a different result and BOOM. At least with unsigned, you have either spotted a bug because the result isn't correct, or if it overflows and works anyway, it'll work on other platforms too. this makes unsigned more resilient to porting. whereas every use of signed needs re-evaluation when porting.

  10. #40
    Join Date
    Apr 2000
    Location
    Belgium (Europe)
    Posts
    4,626

    Re: adding rational number using only class functions

    Quote Originally Posted by 2kaud View Post
    Code:
    void drawPolygon(const std::vector<Point>& points)
    {
        for (size_t i = 1, n = points.size(); i < n; ++i) {
            drawLine(points[i - 1], points[i]);
        }
    }

    Bingo.
    That is the proper way to avoid the overflow entirely.
    And on top of that it's better, because you have moved calculations of your test (every time through the loop) into the initialisation (once) so it's better from a performance POV.

Page 3 of 3 FirstFirst 123

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured