再次浮点精度

时间:2013-03-16 15:14:56

标签: c floating-point floating-accuracy ieee-754 floating-point-precision

昨天我问question我为什么在浮点运算中失去准确性。我收到一个关于如何在x87寄存器中保存中间结果的答案。这很有帮助,但有些细节仍在逃避。这是我在上一个问题中提出的程序的变体,我在调试模式下使用VC ++ 2010 Express。

int main()
{
    double x = 1.8939201459282359e-308; /* subnormal number */
    double tiny = 4.9406564584124654e-324; /* smallest IEEE double */
    double scale = 1.6;
    double temp = scale*tiny;
    printf("%23.16e\n", x + temp);
    printf("%23.16e\n", x + scale*tiny);
}

此输出

1.8939201459282369e-308
1.8939201459282364e-308

根据IEEE标准,第一个值是正确的。赋予scale变量值2.0可为两个计算提供正确的值。我知道第一次计算中的temp是一个次正规值,因此失去了精度。我也理解scale*tiny的值保存在x87寄存器中,该寄存器具有更大的指数范围,因此该值的精度高于temp。我不明白的是,当将值添加到x时,我们从较低的精度值中得到正确的答案。当然,如果较低的精度值可以给出正确的答案,那么更高的精度值也应该给出正确的答案吗?这与“双舍入”有关吗?

先谢谢,这对我来说是一个全新的主题,所以我有点挣扎。

1 个答案:

答案 0 :(得分:7)

关键在于,由于指数范围较大,x87表示中的两个数字不是正常的。

在IEEE754表示中,

x    = 0.d9e66553db96f × 2^(-1022)
tiny = 0.0000000000001 × 2^(-1022)

但是在x87表示中,

x    = 1.b3cccaa7b72de × 2^(-1023)
tiny = 1.0000000000000 × 2^(-1074)

现在,当在IEEE754表示中计算1.6*tiny时,它将四舍五入为0.0000000000002 × 2^(-1022),因为这是与数学结果最接近的可表示数字。将其添加到x会导致

  0.d9e66553db96f × 2^(-1022)
+ 0.0000000000002 × 2^(-1022)
-----------------------------
  0.d9e66553db971 × 2^(-1022)

但在x87表示中,1.6*tiny变为

1.999999999999a × 2^(-1074)

并在添加时

  1.b3cccaa7b72de × 2^(-1023)
+ 0.0000000000003333333333334 × 2^(-1023)
-----------------------------------------
  1.b3cccaa7b72e1333333333334 × 2^(-1023)

结果四舍五入为53位有效位

  1.b3cccaa7b72e1 × 2^(-1023)

使用有效数字1中的最后一位。如果然后将其转换为IEEE754表示(其中有效数据中最多可包含52位,因为它是一个次正规数),因为它恰好位于两个相邻可表示数字之间的中间位置0.d9e66553db970 × 2^(-1022)0.d9e66553db971 × 2^(-1022)默认情况下舍入为有效数字为零的最后一位。

请注意,如果FPU未配置为仅使用53位有效数字,而是使用x87扩展精度类型的完整64位,则添加的结果将更接近IEEE754结果0.d9e66553db971 × 2^(-1022),因此四舍五入。

实际上,由于x87表示具有较大的指数范围,因此即使在有效数字中的位数有限,IEEE754-次正规数的有效数也比IEEE754表示更多。因此,计算结果在x87中比IEEE754中有一个更重要的位。