【问题标题】:Floating point accuracy again再次浮点精度
【发布时间】:2013-03-05 06:14:50
【问题描述】:

昨天我向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 时,我们会从较低的精度值中得到正确的答案。当然,如果较低的精度值可以给出正确的答案,那么较高的精度值也应该给出正确的答案吗?这与“双舍入”有关吗?

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

【问题讨论】:

  • 以下可能是正确的,但对我来说并不明显:当然,如果较低的精度值可以给出正确的答案,那么较高的精度值也应该给出正确的答案?
  • 如果我是你,我会在这样的计算中使用long double...
  • 我们如何知道低精度数的最后一位没有随机值?总是有 10% 的机会达到预期目标。
  • @RondogiannisAristophanes 我的愿望是了解正在发生的事情。
  • @BoPersson 你的评论让我很困惑,没有随机数字,一切都是确定的。加上 IEEE-754 浮点是二进制而不是十进制。

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


【解决方案1】:

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

在 IEEE754 表示中,

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

但在 x87 表示中,

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

现在,当1.6*tiny 以 IEEE754 表示形式计算时,它会四舍五入为 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 中的高一位。

【讨论】:

  • 感谢 Daniel,一个可行的示例真的正是我所需要的。因此,当 1.b3cccaa7b72e1 × 2^(-1023) 转换回 IEEE-754 时,它会向下舍入到 0.d9e66553db970 × 2^(-1022) 而不是 0.d9e66553db971 × 2^(-1022)?一般这个操作的取整模式是什么?
  • 对。 (虽然我根本不知道printf 是否舍入到IEEE754,printf 也可能使用x87 表示。)IEEE754 中的默认舍入模式是round-ties-to-even,即最后一位有效数为零。
  • 嗨丹尼尔,一个小评论:你在x87中描述加法的方式,接近“由于有效位的限制,它变成0.0000000000003×2^(-1023)”听起来像克雷加法( cs.nyu.edu/courses/fall03/G22.2420-001/lec4.pdf)。 x87 所做的在概念上相当于计算确切的总和 (1.b3cccaa7b72e1333333333334 × 2^(-1023)) 然后四舍五入。
  • @PascalCuoq 谢谢,我不确定 x87 在该配置中是如何工作的。
猜你喜欢
  • 1970-01-01
  • 2023-03-31
  • 2018-07-06
  • 2017-11-30
  • 2019-11-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多