3

我有简单的代码:

 #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;
 }

在我的 Ubuntu x64 上,它按预期打印 3.362100e-4932。在我的 NetBSD 上打印 1.681050e-4932

为什么会发生,我该如何解决?我尝试 clang 和 gcc 得到相同的结果。

我的系统(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

供参考

/usr/include/x86/float.h定义为LDBL_MINas 3.3621031431120935063E-4932L并且这个值大于 printf 结果。

4

2 回答 2

3

/usr/include/x86/float.h定义为LDBL_MINas3.3621031431120935063E-4932L并且这个值大于 printf 结果。

LDBL_MIN是 type 的最小正归一化long double。该类型可以表示较小的数字,它们只是次正常的。

我只能推测 NetBSD 问题的性质,但主要有两种可能性:

  1. 编译器将您的初始化常量转换为与请求值相距甚远(相对意义上)的次正规数。

  2. 数字翻译得很好(但结果仍然不正常),NetBSD 的printf()数字不正常,或者至少对于这个数字来说是错误的。

打印的数字是您预期的一半这一事实表明表示中的(二进制)指数存在问题long double。鉴于次正规数的 IEEE 格式的详细信息,很容易想象一个printf()不预期次正规数的实现如何可能会误解(二进制)指数字段以表示比实际表示的指数小一个,因此打印一个值是预期值的一半。这将是我对正在发生的事情的猜测。

您可能还可以通过打印来区分错误的值和错误的展示柜,例如rd * 4. 无论哪种方式,这都应该在正常数字的范围内,因此人们会假设printf()特定于次正常数字的错误不会影响打印它。

至于如何进行,你有几个选择。我最可能想到的是:

  1. 避免低于正常的数字。这可能不切实际,但至少您可以将LDBL_MIN其用作初始化程序,而不是最接近与 subnormal 对应的常量long double

  2. 忽略问题。如果您可以确认这是一个显示问题,而不是错误值问题,那么您可能不需要做任何事情来充分服务于您的更大目标。

  3. 修复 NetBSD 的 C 库。假设问题出在printf(),修复可能不会很大,并且该库是开源的,就像系统的其余部分一样。

  4. 提交错误报告并等待其他人修复它。如果您需要及时修复,那么这可能不合适,但如果您有时间等待,那么这需要您付出很少的努力。

于 2016-01-29T18:16:18.470 回答
2

向 NetBSD 报告 不正常的长双精度值的 printf 结果不正确, 并在 NetBSD-current 中修复

于 2016-03-14T17:20:19.607 回答