#include <cstdio>
int main(void)
{
int val = 500;
printf("%d\n", (int)((long double)val / 500));
printf("%d\n", (int)((long double)500 / 500));
}
Obviously it should output 1 1
. But if you compile it with -Ofast
, it will output 0 1
, why?
And if you change 500
to other values (such as 400
) and compile with -Ofast
, it will still output 1 1
.
Compiler explorer with -Ofast
: https://gcc.godbolt.org/z/YkX7fB
It seems this line causes the problem.
With -Ofast
, -ffast-math
is enabled, which can cause some operations to be calculated in a different and faster way. In your case, (long double)val / 500)
can be calculated as (long double)val * (1.0L / 500))
. This can be seen in the generated assembly when you compare -O2
and -Ofast
for the following function:
long double f(long double a)
{
return a / 500.0L;
}
The assembly generated with -O2
involves fdiv
instruction, while the assembly generated with -Ofast
involves fmul
instruction, see https://gcc.godbolt.org/z/58VHxb.
Next, 1/500, that is, 0.002, is not representable by long double
exactly. Therefore, some rounding occurs and, seemingly, in your case, this rounding happens to be down. This can be checked by the following expression:
500.0L * (1.0L / 500.0L) < 1.0L
which is evaluated as true
: https://gcc.godbolt.org/z/zMcjxJ. So, the exact stored multiplier is 0.002 - some very small delta.
Finally, the result of the multiplication is 500 * (0.002 - delta) = 1 - some small value. And when this value in converted into int
, it's truncated, therefore the result in int
is 0.