In the following code, the functions foo1,foo2 and foo3 are intended to be equivalent. However when run foo3 does not terminate from the loop, is there a reason why this is the case?
template <typename T>
T foo1()
{
T x = T(1);
T y = T(0);
for (;;)
{
if (x == y) break;
y = x;
++x;
}
return x;
}
template <typename T>
T foo2()
{
T x = T(0);
for (;;)
{
T y = x + T(1);
if (!(x != y)) break;
++x;
}
return x;
}
template <typename T>
T foo3()
{
T x = T(0);
while (x != (x + T(1))) ++x;
return x;
}
int main()
{
printf("1 float: %20.5f\n", foo1<float>());
printf("2 float: %20.5f\n", foo2<float>());
printf("3 float: %20.5f\n", foo3<float>());
return 0;
}
Note: This was compiled using VS2010 with /fp precise in release mode. Not sure how GCC etc would treat this code, any information would be great. Could this be an issue where in foo3, the x and x+1 values become NaN somehow?
The IEEE-754 standard describes floating-point formats, a way to represent real numbers in hardware. There are at least five internal formats for floating-point numbers that are representable in hardware targeted by the MSVC compiler. The compiler only uses two of them.
The IEEE 754 standard specifies two precisions for floating-point numbers. Single precision numbers have 32 bits − 1 for the sign, 8 for the exponent, and 23 for the significand. The significand also includes an implied 1 to the left of its radix point.
Hardware floating-point environments and the enhanced floating-point libraries support all four rounding modes. The system chooses the larger of the two possible outputs (that is, the one further from zero if they are positive, and the one closer to zero if they are negative).
IEEE Standard 754 floating point is the most common representation today for real numbers on computers, including Intel-based PC's, Macs, and most Unix platforms. This is as simple as the name. 0 represents a positive number while 1 represents a negative number.
What happens is most likely the following. On the x86 arch, intermediate calculations can be done with 80 bits of precision (long double is the corresponding C/C++ type). The compiler uses all 80 bits for the (+1) operation and for the (!=) operation, but truncates the results before storage.
So what your compiler really does is this:
while ((long double)(x) != ((long double)(x) + (long double)(1))) {
x = (float)((long double)(x) + (long double)(1));
}
This is absolutely non-IEEE-conforming and causes endless headaches for everyone, but this is the default for MSVC. Use /fp:strict
compiler flag to disable this behaviour.
This is my recollection of the problem from about 10 years ago so please forgive me if this is somehow not entirely correct. See this for the official Microsoft documentation.
EDIT I was very surprised to learn that g++ by default exhibits exactly the same behaviour (on i386 linux, but not with e.g. -mfpmath=sse).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With