Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IEEE-754 floating point computations, equality and narrowing

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?

like image 434
Gelly Ristor Avatar asked May 23 '12 03:05

Gelly Ristor


People also ask

What is the IEEE 754 standard for floating-point representation?

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.

What are the key characteristics of the IEEE 754 notation for representing floating-point numbers?

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.

How many rounding modes are available in IEEE 754 standard?

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).

What is the importance of IEEE 754 standard?

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.


1 Answers

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).

like image 162
n. 1.8e9-where's-my-share m. Avatar answered Sep 27 '22 20:09

n. 1.8e9-where's-my-share m.