The C++ standard permits an implementation to evaluate floating-point expressions with more precision than is required by the nominal format. For example, float
expressions may be evaluated as if they were double
or more, and double
expressions may be evaluated as if they were long double
. This extra precision can cause differences in evaluation, especially where discontinuous functions (such as conversion to int
) are used.
For example, given y = 89
, y / 10.0 - y / 10
would be .9 in real-number arithmetic, but it is 0.9000000000000003552713678800500929355621337890625 in double
(IEEE-754 binary64) arithmetic and 0.89999999999999999965305530480463858111761510372161865234375 in long double
(Intel’s 80-bit format) arithmetic, and then multiplying by 10 and converting to int
produces 9 or 8, respectively.
With optimization disabled, the compiler may be evaluating the expression with y
at run-time and the expression with 89
during compilation, and it may be using different precisions for them. With optimization, the compiler is likely to recognize y
is effectively a constant 89
in the first expression and evaluate both expressions during compilation, use the same method for both.
The C++ standard requires that cast and assignment operations convert to the nominal type, so one test to see if this is happening is to insert casts:
int foo = (double) ((double) (y / 10.0) - y / 10) * 10;
int bar = (double) ((double) (89 / 10.0) - 89 / 10) * 10;
If this results in foo
and bar
being identical, that supports the hypothesis. Your compiler may have switches to control how floating-point expressions are evaluated.
Another test is to include <cfloat>
and print the value of FLT_EVAL_METHOD
. If it is 0, the implementation claims to evaluate floating-point operations in their nominal type, and this behavior should not occur. If it is 1 or 2, the implementation claims to use double
or long double
to evaluate double
expressions, respectively, and this behavior should again not occur, since both expressions will be evaluated the same way. If it is −1, the implementation is not making these claims, and the behavior may occur.
To get the last digit of a non-negative integer, use y % 10
.