提问人:zg c 提问时间:7/18/2023 最后编辑:zg c 更新时间:7/18/2023 访问量:88
有没有一种方法可以减轻舍入误差?
Is there one way to alleviate roundoff errors?
问:
关于“保护位”的维基百科提供了一个示例代码:
#include <stdio.h>
int main(){
double a;
int i;
a = 0.2;
a += 0.1;
a -= 0.3;
for (i = 0; a < 1.0; i++)
a += a;
printf("i=%d, a=%f\n", i, a);
return 0;
}
使用我的zen2 r7 4800h cpu,我通过编译了上述源代码。然后它输出与维基百科相同。Guard_digit.c
gcc Guard_digit.c -std=c17 -march=znver2 -pedantic -O0 -o With_Guard_digit.o
i=54, a=1.000000
正如本注释所说,IEEE标准已经实现了保护数字:
IEEE 标准要求使用 3 个意义较小的额外位 比单精度中隐含的 24 位(尾数)要多 表示法。
尾数格式加上额外的位:
1.XXXXXXXXXXXXXXXXXXXXXXX 0 0 0 ^ ^ ^ ^ ^ | | | | | | | | | - sticky bit (s) | | | - round bit (r) | | - guard bit (g) | - 23 bit mantissa from a representation - hidden bit
问:有没有一种方法可以通过更改源代码或其他代码来解决这个精度和舍入问题(即误差偏移可以在一定程度上减轻,以便它可以输出类似的东西)?i=108, a=1.000000
查看 Eric Postpischil 的答案后编辑:
很抱歉没有清楚地描述问题。我想知道如何通过保留原始计算来解决舍入问题,因此不考虑直接计算。a = 0;
我想解决这个具体问题,但不是一般的。正如评论所说,这超出了我目前的范围。
答:
问:有没有一种方法可以通过更改源代码或其他代码来解决这个精度和舍入问题(即误差偏移可以在一定程度上减轻,以便它可以输出类似的东西)?
i=108, a=1.000000
在常见的 C 实现中,不可能通过添加和/或减去 .0625 或更高的值来生成,这将导致所示循环在迭代超过 57 次后终止。a
这是因为常见的 C 实现使用 IEEE-754 binary64(也称为“双精度”)表示 ,而 binary64 使用 53 位有效位。这意味着 binade 中以 .0625 开头的值用一个有效位表示,其高位的位置值为 2−4 (.0625),其低位的位置值为 2−56(跨越 53 位,包括两个端点)。double
加法和减法可以将位带到高位,就像小学算术中教授的那样,但永远不会在最低输入位置以下产生非零位。因此,通过添加和减去大于或等于 .0625 的值生成的任何结果都不能有任何低于 2−56 的非零位。
因此,在执行此类算术后进入循环时,我们有以下情况之一:
a
为负数或零,并且循环永不终止。a
是 2−56 或更大,迭代 57 次或更少将使其大于 1。
有没有一种方法可以通过更改源代码来解决这个精度和舍入问题......
显然,0.2 + 0.1 - 0.3 的正确结果可以通过将源代码从以下位置更改为:
a = 0.2;
a += 0.1;
a -= 0.3;
自:
a = 0;
这是计算中的一个常见问题:你不能通过问“我如何得到这些值的解决方案?”来正式描述你想要解决的一般问题,因为这样就有一个简单的解决方案,它只是这些值的一个答案,它对你没有普遍的帮助。
相反,您必须描述整个问题类别。例如,您可以问:“如何编写代码来查找最多 30 个正负十进制数字的精确十进制和,小数点后最多三位数字,小数点前最多两位数?
进一步注意,你不希望在另一个方向上走得太远,使问题完全笼统而不是完全具体。如果问题是在没有错误的情况下添加和减去任何十进制数字,则必须编写任意精度算术。如果问题是用一些适度数量的数字相加和相减一些适度数量的十进制数字,那么这个问题可以通过使用算术和精心选择的舍入来解决。具体的解决方案可能取决于您选择的参数。因此,您需要很好地描述问题的特征。double
评论
上一个:浮点数学坏了吗?
下一个:C语言中梯度下降的数值不稳定性
评论
long double
常量需要再迭代一次,超出常量在 Godbolt 上允许的处理时间......不过,我还是认为该程序不会无休止地运行,只是将错误累积到达到 1.0 花费的时间太长了。double
long double
printf("i=%d, a=%f\n", i, a);
printf("i=%d, a=%f %a\n", i, a, a);