4

编辑:我在调试期间犯了一个错误,导致我问这个问题。我看到的差异实际上在于打印双精度和解析双精度 ( strtod)。即使经过这次整改,斯蒂芬的回答仍然很好地涵盖了我的问题,所以我想我会不理会这个问题,以防它对某人有用。

我可以访问的一些(大多数)C 编译平台在以下情况下不考虑 FPU 舍入模式

  • 将 64 位整数转换为double;
  • 打印一个double.

这里没有什么特别的东西:Mac OS X Leopard、各种最新的 Linux 和 BSD 变体、Windows。

另一方面,Mac OS X Snow Leopard 在做这两件事时似乎将舍入模式考虑在内。当然,有不同的行为会让我烦恼不已。

以下是这两种情况的典型片段:

#if defined(__OpenBSD__) || defined(__NetBSD__) 
# include <ieeefp.h>
# define FE_UPWARD FP_RP
# define fesetround(RM) fpsetround(RM)
#else 
# include <fenv.h>
#endif

#include <float.h>
#include <math.h>

fesetround(FE_UPWARD);

...
double f;
long long b = 2000000001;
b = b*b;
f = b;

...
printf("%f\n", 0.1);

我的问题是:

  1. 我可以做一些不丑陋的事情来规范所有平台的行为吗?一些隐藏的设置告诉平台不考虑舍入模式,反之亦然?
  2. 是行为标准之一吗?
  3. 不使用 FPU 舍入模式时,我可能会遇到什么情况?向零舍入?四舍五入到最近?请告诉我只有一种选择:)

关于 2. 我在标准中找到了一个地方,据说转换为整数的浮点数总是被截断(向零舍入),但我找不到整数 -> 浮点方向的任何内容。

4

1 回答 1

4

如果你没有设置取整模式,应该是IEEE-754默认模式,就是四舍五入。

对于从整数到浮点数的转换,C 标准说(§6.3.1.4):

当整数类型的值转换为真正的浮点类型时,如果被转换的值可以在新类型中精确表示,则它是不变的。如果要转换的值在可以表示但不能准确表示的值范围内,则结果是最接近的较高或最近的较低可表示值,以实现定义的方式选择。如果要转换的值超出可表示的值范围,则行为未定义。

所以这两种行为都符合 C 标准。

C 标准规定(§F.5)IEC60559 浮点格式和字符序列之间的转换按照 IEEE-754 标准正确舍入。对于非 IEC60559 格式,这是推荐的,但不是必需的。1985 年 IEEE-754 标准说(条款 5.4):

对于位于表 3 指定范围内的操作数,转换应按照第 4 节的规定正确舍入。否则,为了舍入到最接近,转换结果中的误差不应超过目标最低有效位误差的 0.47 个单位这是由第 4 节的舍入规范引起的,前提是不发生指数上溢/下溢。在定向舍入模式下,误差应有正确的符号,并且在最后一位不得超过 1.47 个单位。

第(4)条实际上说的是,操作应按照现行的舍入模式进行。即,如果您更改舍入模式,IEEE-754 表示 float->string 转换的结果应相应更改。整数->浮点数转换同上。

IEEE-754 标准的 2008 年修订版说(条款 4.3):

舍入方向属性会影响所有可能不精确的计算操作。不精确的数值浮点结果始终与未舍入结果具有相同的符号。

两种转换都被定义为第 5 节中的计算操作,因此它们应再次根据流行的舍入模式执行。

我认为 Snow Leopard 在这里具有正确的行为(假设它根据当前的舍入模式正确舍入了结果)。如果您想强制执行旧行为,printf我想您始终可以将调用包装在更改舍入模式的代码中,尽管这显然不理想。

或者,您可以%a在符合 C99 的平台上使用格式说明符(十六进制浮点)。由于这种转换的结果总是准确的,它永远不会受到流行的舍入模式的影响。我不认为 Windows C 库支持%a,但如果你需要,你可以很容易地移植 BSD 或 glibc 实现。

于 2010-04-07T20:54:44.420 回答