【问题标题】:How are results rounded in floating-point arithmetic?浮点运算中的结果如何四舍五入?
【发布时间】:2018-08-17 22:03:44
【问题描述】:

我编写了这段代码,它只是简单地将 n 个数字的列表相加,以练习浮点运算,但我不明白:

我正在使用浮点数,这意味着我有 7 位精度,因此,如果我执行 10002*10002=100040004 操作,数据类型浮点数的结果将为 100040000.000000,因为我丢失了第 7 位以外的任何数字(程序仍然知道指数,如 here)。

如果这个程序中的输入是

3
10000
10001
10002

但是,您会看到,当这个程序计算 30003*30003=900180009 时,我们有 30003*30003=900180032.000000

我理解这个 32 出现是因为我正在使用 float,我的目标不是让程序更精确,而是理解为什么会发生这种情况。为什么是 900180032.000000 而不是 900180000.000000?为什么即使数字的大小相同,这个十进制噪声(32)也会出现在 30003*30003 而不是 10002*10002 中?感谢您的宝贵时间。

#include <stdio.h>
#include <math.h>
#define MAX_SIZE 200


int main() 
{
int numbers[MAX_SIZE]; 
int i, N;
float sum=0;
float sumb=0;
float sumc=0;

printf("introduce n" );
scanf("%d", &N);

printf("write %d numbers:\n", N);
for(i=0; i<N; i++)
{
    scanf("%d", &numbers[i]);
}

int r=0;

while (r<N){
    sum=sum+numbers[r];
    sumb=sumb+(numbers[r]*numbers[r]); 
    printf("sum is %f\n",sum);
    printf("sumb is %f\n",sumb);
    r++;
}
sumc=(sum*sum);
printf("sumc is %f\n",sumc);
}

【问题讨论】:

  • 有时,这与使用的表示形式和可用的位一样接近。它是浮点数。它发生了......
  • 求和时浮点累加错误。您可以使用double 来降低效果
  • 不久前我还建议您联系this answer I wrote
  • 你的数字大小不一样。它们相差 9 倍,这意味着二进制表示需要 3-4 个额外的位。因此,通过四舍五入到可以存储的下一个更高或更低值引入的误差会大 8 或 16 倍。虽然将第一个数字四舍五入会导致 -4 的误差,但第二个数字可能是 +/-64号码。
  • 不,100040000.000000 有 15 个有效数字。浮点数在尾数中有 24 位(23 存储 + 1 隐式),因此只能表示 2^24 = 16777216 个不同的值。或 log10(16777216) = 7.2 位有效数字。由于每次转换和计算的舍入,您会损失 0.5 位。因此,当您从十进制转换为二进制并再次转换回十进制以显示该值时,您最多只剩下 23 位精度。 log10(2^23) = 6 位有效数字。残酷的数学,如果在尾数中多一点浮点数,效果会更好。

标签: c floating-point


【解决方案1】:

如下所述,10002 乘以 10002 的计算结果必须是 8 的倍数,而 30003 乘以 30003 的计算结果必须是 64 的倍数,因为数字的大小和位数可代表他们。尽管您的问题是关于“十进制噪声”,但这里没有涉及十进制数字。结果完全是由于四舍五入到 2 的幂的倍数。 (您的 C 实现似乎使用通用 IEEE 754 格式的二进制浮点数。)

当您将 10,002 乘以 10,002 时,计算结果必须是 8 的倍数。我将在下面解释原因。数学结果是 100,040,004。最接近的 8 倍数是 100,040,000 和 100,040,008。它们与精确结果相差甚远,用于打破平局的规则选择偶数倍数(100,040,000 是 12,505,000 的八倍,偶数,而 100,040,008 是 12,505,001 的八倍,奇数)。

许多 C 实现对 float 使用 IEEE 754 32 位基本二进制浮点。在这种格式中,数字表示为整数 M 乘以 2 的幂 e。整数 M 的大小必须小于 224。指数 e 可以从 -149 到 104。这些限制来自用于表示整数和指数的位数。

因此,这种格式的所有float 值都具有 M • 2e 对于某些 M还有一些e。格式中没有十进制数字,只有一个整数乘以 2 的幂。

考虑数字 100,040,004。我们可以使用的最大 M 是 16,777,215 (224-1)。这还不够大,我们可以将 100,040,004 写为 M • 20。所以我们必须增加指数。即使使用 22,我们能得到的最大数也是 16,777,215 • 22 = 67,108,860。所以我们必须使用 23。这就是为什么在这种情况下,计算结果必须是 8 的倍数。

因此,要在float 中生成 10,002•10,002 的结果,计算机使用 12,505,000 • 23,即 100,040,000。

在 30,003•30,003 中,结果必须是 64 的倍数。确切的结果是 900,180,009。 25 是不够的,因为 16,777,215•25 是 536,870,880。所以我们需要 26,即 64。64 的两个最接近的倍数是 900,179,968 和 900,180,032。在这种情况下,后者更接近(23 远比 41 远),因此选择了它。

(虽然我将格式描述为整数乘以 2 的幂,但也可以将其描述为二进制数字,小数点前有 1 个二进制数,小数点后有 23 个二进制数,调整指数范围以进行补偿. 这些在数学上是等价的。IEEE 754 标准使用后一种描述。教科书可能使用前一种描述,因为它使分析一些数值属性更容易。)

【讨论】:

  • 感谢您的回答,这很清楚,但是,为什么计算机要选择 $M=16,777,215$?它可以选择一个较小的数字但较大的指数 $e$,然后更精确地计算结果。
  • @codingnight:假设你有更大的指数,比如 24 而不是 23。那么这个数字必须是 16 的倍数,而不是 8 的倍数。这使得它更具限制性。每个 16 的倍数都是 8 的倍数。对于较小的值之间的间距,您需要较低的指数。增加指数的唯一原因是数字的大小需要它。
【解决方案2】:

浮点运算以二进制而不是十进制完成。

浮点数实际上有 24 个二进制精度位,其中 1 个是符号位,其中 23 个称为有效位。这将转换为大约 7 个十进制数字的精度。

您正在查看的号码 900180032 已经是 9 位数字,因此最后两位数字(32)可能是错误的。像算术一样的舍入是用二进制完成的,只有将事物分解成二进制才能看到舍入差异的原因。

900180032 = 110101101001111010100001000000

900180000 = 110101101001111010100000100000

如果您从每个数字中的第一个 1 到最后一个 1(我用粗体表示的部分)计算,那就是存储该数字需要多少有效位。 900180032 只需要 23 个有效位来存储,而 900180000 需要 24 个有效位,这使得 900180000 成为不可能存储的数字,因为浮点数只有 23 个有效位。 900180032 是浮点数可以存储的最接近正确答案 900180009 的数字。

在另一个例子中

100040000 = 101111101100111110101000000

100040004 = 101111101100111110101000100

正确答案,100040004 有 25 个有效位,对于浮点数来说太多了。具有 23 个或更少有效位的最接近的数字是 10004000,它只有 21 个有效位。

有关浮点运算的更多信息,请点击此处http://steve.hollasch.net/cgindex/coding/ieeefloat.html

【讨论】:

  • 浮点运算并不总是以二进制形式完成。它也是十进制的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-22
  • 1970-01-01
  • 1970-01-01
  • 2013-02-04
  • 1970-01-01
相关资源
最近更新 更多