Thread: Who is right?

  1. #1
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544

    Who is right?

    Code:
    	ULONGLONG ll1 = 50000ull;
    	LONGLONG ll2  = 99000ll;
    	LONGLONG ll3;
    
    	if ((ll1 - ll2) < 0) printf("< 0\n");
    	else printf("> 0\n");
    
    	ll3 = ll1 - ll2;
    	if (ll3 < 0) printf("< 0\n");
    	else printf("> 0\n");
    MSVC Output:
    > 0
    < 0
    Lcc Output:
    < 0
    < 0
    Who is right here, or is it undefined?

    EDIT: :-; Fixed. I don't have an entry function either.
    Last edited by anonytmouse; 12-09-2003 at 08:17 AM.

  2. #2
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    Neither of them are right. You don't actually have a variable called ll in your example. It won't compile.

    Quzah.
    Hope is the first step on the road to disappointment.

  3. #3
    Code Goddess Prelude's Avatar
    Join Date
    Sep 2001
    Posts
    9,897
    I'll ignore the fact that your code won't compile.

    >Who is right here, or is it undefined?
    Conversion of arithmetic types can be complicated.

    >(ll - ll2)
    As an expression, the usual conversions apply, and the type of the result is the type of the longest, floatiest operand. So the result of this expression will be unsigned long long. Because unsigned types roll over when they reach their boundaries, the result of this expression is a very large unsigned value, thus, greater than 0.

    >ll3 = ll - ll2;
    This is completely different from the above. Note that the right hand side of the expression is the same as above, but explicit assignment to a signed type is considered an overflow and is technically undefined even though a lot of programs rely on a quiet conversion that results in a negative number. So a reasonable result would be less than 0, but since it is undefined behavior, you can't rely on that.
    My best code is written with the delete key.

  4. #4
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    I'll ignore the fact that your code won't compile.
    While we're at it, types are undefined too!

    As an expression, the usual conversions apply, and the type of the result is the type of the longest, floatiest operand. So the result of this expression will be unsigned long long. Because unsigned types roll over when they reach their boundaries, the result of this expression is a very large unsigned value, thus, greater than 0.

    This is what I figured. So LCC is wrong.

    This is completely different from the above. Note that the right hand side of the expression is the same as above, but explicit assignment to a signed type is considered an overflow and is technically undefined even though a lot of programs rely on a quiet conversion that results in a negative number. So a reasonable result would be less than 0, but since it is undefined behavior, you can't rely on that.

    So is there any 'correct' way to get a signed result from an unsigned operation.

  5. #5
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    Originally posted by anonytmouse

    This is what I figured. So LCC is wrong.
    No. That's the thing with 'undefined'. There is no "wrong". It simply is undefined. That means they could make it output "This is right!" to a new file called "undefined.txt" whenver this situation occurred, and it wouldn't be "wrong". It's undefined. It can be anything. It can do anything. There is no wrong. It simply has no defined reaction to said situation. Thus the compiler maker can do whatever they feel like and still have a fully ANSI compatible compiler.

    Originally posted by anonytmouse
    So is there any 'correct' way to get a signed result from an unsigned operation.
    That depends. What do you define as "correct"? Do you want it to wrap around? Do you want it to be zero if it isn't accurate? Or what? There is no correct. You could do something like:
    Code:
    signed int signme( unsigned int i )
    {
        if( i < maximum_unsigned_value / 2 )
            return (signed int) i;
        else
            return -1;
    }
    -1 being an error. Or, you could then instead subtract 1/2 maximum unsigned value from i and then subtract the remainder of that from zero. Or... See the problem with undefined behavior?

    Quzah.
    Hope is the first step on the road to disappointment.

  6. #6
    Code Goddess Prelude's Avatar
    Join Date
    Sep 2001
    Posts
    9,897
    >So is there any 'correct' way to get a signed result from an unsigned operation.
    Unless the result has a valid signed representation, no. In other words:
    Code:
    unsigned long l1 = 100UL;
    unsigned long l2 = 50UL;
    long l3;
    
    l3 = l1 - l2;
    The result is an unsigned value (50), but when converted to a signed value, the representation is the same. So the above expression is legal and well defined. However, if you reverse the expression:
    Code:
    unsigned long l1 = 100UL;
    unsigned long l2 = 50UL;
    long l3;
    
    l3 = l2 - l1;
    The result is an unsigned value (4294967246 on my machine), but this is outside of the boundaries for a signed long, so overflow occurs and the result is undefined.
    My best code is written with the delete key.

  7. #7
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    So wrapping is defined behavior, but conversion isn't?
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  8. #8
    Code Goddess Prelude's Avatar
    Join Date
    Sep 2001
    Posts
    9,897
    >So wrapping is defined behavior, but conversion isn't?
    Unsigned wrapping is defined behavior, but signed overflow is not. As I said, conversions can be complicated.
    My best code is written with the delete key.

  9. #9
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    No. That's the thing with 'undefined'. There is no "wrong". It simply is undefined.

    Can I just clarify what exactly is undefined here? Am I correct in saying that in the context of the operation it is defined that both values are treated as unsigned and that the undefined part is what to do with the overflow?

    Unless the result has a valid signed representation, no.

    So if we want to reliably and portably get a signed result we must, in theory, use signed values.

    So we change:
    Code:
    short = ushort - ushort
    /* to: */
    long = long - long
    The trouble is using long long we don't have that option.

    Code:
    	ll3 = ll1 - ll2;
    	if (ll3 < 0) printf("< 0\n");
    	else printf("> 0\n");
    In practise, on the Windows platform, will this always give the 'correct'(as in the real world) result (where ll2 is less than signed_long_long_max)? I don't seem to have much choice.

    Thanks.

  10. #10
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    If your compiler documents its behavior, you can use it. But don't forget that your app is not portable anymore.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  11. #11
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    But don't forget that your app is not portable anymore.

    In theory, but I'm wondering if there is a Windows compiler with differing behaviour for this example.

    Someone could have a useful website documenting these behaviours.

  12. #12
    Code Goddess Prelude's Avatar
    Join Date
    Sep 2001
    Posts
    9,897
    >Someone could have a useful website documenting these behaviours.
    Wouldn't that be an exercise in futility.
    My best code is written with the delete key.

  13. #13
    Registered User
    Join Date
    Oct 2001
    Posts
    2,934
    > if ((ll1 - ll2) < 0) printf("< 0\n");

    The question is: which operand gets converted before the subtraction is performed? Is ll1 converted to a LONGLONG, or is ll2 converted to a ULONGLONG?

    If ULONGLONG - ULONGLONG, then MSVC is right, as the result has to be unsigned.
    If LONGLONG - LONGLONG, then Lcc is right (assuming LONGLONG is signed).

    Does the C standard say what conversion occurs before the subtraction, or is it up to the compiler?

  14. #14
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    > Does the C standard say what conversion occurs before the subtraction, or is it up to the compiler?
    It's specified by the standard
    Also K&R2 Page 198 Para A.6 also explains it
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  15. #15
    Registered User
    Join Date
    Oct 2001
    Posts
    2,934
    >It's specified by the standard
    >Also K&R2 Page 198 Para A.6 also explains it

    Quoting from the draft:
    From 6.3.1.1
    — The rank of any unsigned integer type shall equal the rank of the corresponding
    signed integer type, if any.
    From 6.3.1.8
    If both operands have the same type, then no further conversion is needed.

    Otherwise, if both operands have signed integer types or both have unsigned
    integer types, the operand with the type of lesser integer conversion rank is
    converted to the type of the operand with greater rank.

    Otherwise, if the operand that has unsigned integer type has rank greater or
    equal to the rank of the type of the other operand, then the operand with
    signed integer type is converted to the type of the operand with unsigned
    integer type.
    From what I can tell, MSVC is right. But I may have misinterpreted what I read.

Popular pages Recent additions subscribe to a feed