Search code examples
cfloating-pointnetbsd

NetBSD long double trouble


I have simple code:

 #include <stdio.h>

 int main()
 {
      //char d[10] = {0x13, 0x43, 0x9b, 0x64, 0x28, 0xf8, 0xff, 0x7f, 0x00, 0x00};
      //long double rd = *(long double*)&d;
      long double rd = 3.3621e-4932L;
      printf("%Le\n", rd);
      return 0;
 }

On my Ubuntu x64 it prints as expected 3.362100e-4932. On my NetBSD it prints 1.681050e-4932

Why it happens and how can I fix it? I try clang and gcc with same result.

My system (VM inside VirtualBox 5.0):

 uname -a
 NetBSD netbsd.home 7.0 NetBSD 7.0 (GENERIC.201509250726Z) amd64

 gcc --version
 gcc (nb2 20150115) 4.8.4

 clang --version
 clang version 3.6.2 (tags/RELEASE_362/final)
 Target: x86_64--netbsd
 Thread model: posix

FYI

/usr/include/x86/float.h defines as LDBL_MIN as 3.3621031431120935063E-4932L And this value is greater than printf result.


Solution

  • /usr/include/x86/float.h defines as LDBL_MIN as 3.3621031431120935063E-4932L And this value is greater than printf result.

    LDBL_MIN is the minimum positive normalized value of type long double. The type can represent smaller numbers, they are just subnormal.

    I can only speculate about the nature of the problem on NetBSD, but there are two main possibilities:

    1. The compiler translates your initialization constant into a subnormal number that is rather far (in a relative sense) from the requested value.

    2. The number is translated fine (but the result is still subnormal), and NetBSD's printf() is buggy for subnormal numbers, or at least for this one.

    The fact that the number printed is half of what you expect suggests a problem with the (binary) exponent in the long double representation. Given the details of the IEEE format for subnormal numbers, it is easy to imagine how a printf() implementation that does not anticipate subnormal numbers could misinterpret the (binary) exponent field to represent an exponent one less than what actually is represented, and therefore to print a value that is half of the expected one. This would be my guess as to what is happening.

    You can probably distinguish between the wrong value and wrong display cases by also printing, say, rd * 4. That should be in the range of normal numbers either way, so one would presume that a printf() bug specific to subnormal numbers would not affect printing it.

    As for how to proceed, you have several choices. The most likely ones that occur to me are:

    1. Avoid subnormal numbers. This might not be practical, but at least you could use LDBL_MIN as your initializer instead of a constant that corresponds most closely to a subnormal long double.

    2. Ignore the problem. If you can confirm that it is a display issue, as opposed to a wrong-value issue, then it may be that you don't need to do anything to adequately serve your larger objective.

    3. Fix NetBSD's C library. Supposing that the problem is in printf(), the fix would probably not be very big, and the library is open source, like the rest of the system.

    4. File a bug report and wait for someone else to fix it. If you need a timely fix then this might not be suitable, but if you have time to wait then this requires pretty little effort on your part.