Red Hat Bugzilla – Bug 210529
64-bit signed / unsigned division gives the wrong result on RHEL4 64-bit with gcc 3.4.6
Last modified: 2007-11-30 17:07:27 EST
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):
Compile & run attached program. Observe incorrect results.
Steps to Reproduce:
1. $ gcc divide_bug.cpp -lstdc++
2. $ ./a.out
See output of test program (large unsigned value returned on our system).
Expecting a correctly computed result (-40, in this case).
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, 18.104.22.168:
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
- 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
- Otherwise, if either operand is float, the other shall be converted to
- Otherwise, the integral promotions (4.5) shall be performed on both
- Then, if either operand is unsigned long the other shall be converted to
- 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
- Otherwise, if either operand is unsigned, the other shall be converted
[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
-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
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,
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.