这是我的问题的伪代码。
我有一组 IEEE 754 双精度正数。
数组可以按随机顺序排列,但数字总是相同的,只是在它们的位置上乱码。此类数字在double
表示的有效IEEE范围内可以在很宽的范围内变化。
获得列表后,我会初始化一个变量:
double sum_result = 0.0;
我在sum_result
上累积总和,在整个数组的循环中。在每一步我都做:
sum_result += my_double_array[i]
是否保证,无论double
的初始数组的顺序如何,如果数字相同,打印出的总和结果将始终相同?
答案 0 :(得分:2)
没有
举一个简单的例子,将1加到0x1p53会产生0x1p53。 (这使用十六进制浮点表示法。“p”之前的部分是有效数字,以十六进制表示,与C十六进制整数常量相同,除了它可能有一个“。”标记分数的开头“p”后面的数字表示有效数乘以2的幂。)这是因为数学上精确的和,0x1.00000000000008p + 53,不能用IEEE-754 64位二进制浮点表示,因此它的四舍五入到最接近的值,其有效位数为偶数位,即0x1p53。
因此,0x1p53 + 1产生0x1p53。所以0x1p53 + 1 + 1,从左到右计算,也产生0x1p53。但是,1 + 1是2,并且2 + 0x1p53可以准确表示,因为0x1.0000000000001p + 53,所以1 + 1 + 0x1p53是0x1.0000000000001p + 53。
为了以十进制显示更容易可视化的示例,假设我们只有两位小数。然后100 + 1产生100,所以100 + 1 + 1 + 1 + 1 + 1 + 1产生100.但是1 + 1 + 1 + 1 + 1 + 1 + 100累积到6 + 100然后产生110(由于四舍五入到两位有效数字)。
答案 1 :(得分:1)
是否保证,无论初始数组double的顺序如何,如果数字相同,打印出的总和结果将始终相同?
不,FP添加不是associative。记住它被称为浮动点 - 绝对精度"浮动"约相对于1.0。任何给定的operation添加内容(+
)都受round-off error的约束。
然而,如果总和完成并且不精确标志是明确的,那么是,订单不相关。**
简单的反例。
#include <math.h>
#include <float.h>
#include <fenv.h>
#include <stdio.h>
int main(void) {
double a[3] = { DBL_MAX, -DBL_MAX, 1.0 };
fexcept_t flag;
feclearexcept(FE_ALL_EXCEPT);
printf("%e\n", (a[0] + a[1]) + a[2]);
fegetexceptflag(&flag, FE_INEXACT);
printf("Inexact %d\n", !!(flag & FE_INEXACT));
feclearexcept(FE_ALL_EXCEPT);
printf("%e\n", a[0] + (a[1] + a[2]));
fegetexceptflag(&flag, FE_INEXACT);
printf("Inexact %d\n", !!(flag & FE_INEXACT));
printf("%d\n", FLT_EVAL_METHOD);
return (EXIT_SUCCESS);
}
输出
1.000000e+00 // Sum is exact
Inexact 0
0.000000e+00 // Sum is inexact
Inexact 1
0 // evaluate all operations ... just to the range and precision of the type;
根据FLT_EVAL_METHOD
,FP数学可能会使用更宽的进动和范围,但上面的极端示例总和仍然会有所不同。
**除了可能是0.0 vs -0.0
的结果要了解原因,请尝试使用4位精度的基于10的文本示例。同样的原则适用于double
及其通常的53位精确二进制数字。
a[3] = +1.000e99, -1.000e99, 1.000
sum = a[0] + a[1] // sum now exactly 0.0
sum += a[2] // sum now exactly 1.0
// vs.
sum = a[1] + a[2] // sum now inexactly -1.000e99
sum += a[0] // sum now inexactly 0.0
Re:&#34;打印出的总和结果将始终相同&#34; :除非代码打印的"%a"
或"%.*e"
具有更高的精度,否则打印的文本可能缺乏显着性,而两个不同的总和可能看起来相同。见Printf width specifier to maintain precision of floating-point value
答案 2 :(得分:1)
让我们举一个例子:我使用基数为10的模型转换浮点问题,只有2位有效数字,操作结果四舍五入到最近。
假设我们必须总结3个数字9.9 + 8.4 + 1.4
确切的结果是19.7
,但我们只有两位数,所以它应该舍入为20.
如果我们先对9.9 + 8.4
求和,我们会得到18.3
,然后四舍五入为18.
然后我们总结18. + 1.4
我们将19.4
四舍五入为19.
。
如果我们首先对最后两个词8.4 + 1.4
求和,我们得到9.8
,则不需要舍入。
然后9.9 + 9.8
我们将19.7
四舍五入为20.
,结果不同。
(9.9 + 8.4) + 1.4
与9.9 + (8.4 + 1.4)
不同,sum运算不是关联的,这是由于中间舍入。我们也可以用其他舍入模式展示类似的例子......
问题在基数2中与53位有效数字完全相同:无论基数或有效长度如何,中间舍入都将导致非关联性。
要消除这个问题,您可以对数字进行排序,使顺序总是相同,或者消除中间舍入并仅保留最后一个,例如使用超级累加器,例如https://arxiv.org/pdf/1505.05571.pdf
...或者只是接受一个近似的结果(由您来分析平均或更差的错误并决定是否可以接受......)。