Description of problem: On RHEL4 64 bit with gcc 3.4.6, 64 bit signed / unsigned division gives the wrong result (AMD64). The compiler is incorrectly generating an unsigned divide instruction (divq) instead of a signed divide (idivq). Version-Release number of selected component (if applicable): gcc-3.4.6-3 How reproducible: Compile & run attached program. Observe incorrect results. Steps to Reproduce: 1. $ gcc divide_bug.cpp -lstdc++ 2. $ ./a.out Actual results: See output of test program (large unsigned value returned on our system). Expected results: Expecting a correctly computed result (-40, in this case). Additional info: Sample test program (divide_bug.cpp) attached.
Created attachment 138353 [details] test program for divide bug
Please read the relevant portions of the standards (ISO C++98 doesn't include long long type, it is just a G++ extension, so you need to read both ISO C99 (which has it) and ISO C++98): ISO C99, 6.3.1.8: ... Otherwise, the integer promotions are performed on both operands. Then the following rules are applied to the promoted operands: 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. Otherwise, if the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, then the operand with unsigned integer type is converted to the type of the operand with signed integer type. Otherwise, both operands are converted to the unsigned integer type corresponding to the type of the operand with signed integer type. ISO C++98, [expr]/9: Many binary operators that expect operands of arithmetic or enumeration type cause conversions and yield result types in a similar way. The purpose is to yield a common type, which is also the type of the result. This pattern is called the usual arithmetic conversions, which are defined as follows: - If either operand is of type long double, the other shall be converted to long double. - Otherwise, if either operand is double, the other shall be converted to double. - Otherwise, if either operand is float, the other shall be converted to float. - Otherwise, the integral promotions (4.5) shall be performed on both operands. - Then, if either operand is unsigned long the other shall be converted to unsigned long. - Otherwise, if one operand is a long int and the other unsigned int, then if a long int can represent all the values of an unsigned int, the unsigned int shall be converted to a long int; otherwise both operands shall be converted to unsigned long int. - Otherwise, if either operand is long, the other shall be converted to long. - Otherwise, if either operand is unsigned, the other shall be converted to unsigned. [Note: otherwise, the only remaining case is that both operands are int] So, in your case, the first operand of / needs to be converted to unsigned long long, because div1 is unsigned long long, the division is done as unsigned and in the end converted to long long for the assignment.
With all due respect... If you run the attached program, then you will get the following results: $ ./a.out -120 / 3 = 6148914691236517165 IMHO, this makes no sense.
-120 / 3 is really -40, both are signed. But that's not what you are using in the divide. It is -120LL / 3ULL, which is according to usual standard conversions converted to -120ULL / 3ULL, because there is no such thing as signed/unsinged division, C has either signed division (if both / operators are signed or converted to signed), or unsigned division. And, as -120ULL is 18446744073709551496 and 18446744073709551496ULL / 3ULL really is 6148914691236517165ULL. If you want signed divide, then just cast the second operand to a signed type, i.e. long long res = orig / (long long) div1; This isn't something where personal opinions should matter, the standard has clear rules what happens when different types are used in the binary operators.
Well, I understand these conversions... And I love standards ... but only then it makes sense :) the above does not make any sense. Probably, compiler should generate a warning ... BTW, this is not a question of personal opinions. The program (compiler) does something that user does not expect and that can cause severe bugs.