为什么具有精确值的浮点数之和仍然取决于顺序?
Why does the sum of float numbers with exact values still depend on order?
假设我有3个浮点数,可以用浮点数准确表示:
var a=0.1000000000000000055511151231257827021181583404541015625;
var b=0.200000000000000011102230246251565404236316680908203125;
var c=0.299999999999999988897769753748434595763683319091796875;
它们是0.1、0.2、0.3实际存储在float中的值。我认为a+b+c应该等于c+b+a,因为a、b和c都已经四舍五入了,而且是精确值,它们的和应该不依赖于顺序,但现在我测试了它不是:
var a=0.1000000000000000055511151231257827021181583404541015625;
var b=0.200000000000000011102230246251565404236316680908203125;
var c=0.299999999999999988897769753748434595763683319091796875;
console.log(a+b+c==c+b+a);
这是什么原因?
注意:我不是在问 Is floating point math broken?, because 0.1000000000000000055511151231257827021181583404541015625 is already the accurate value in floating point number (some float numbers can be represented without rounding errors, for example 0.5,0.75 and 0.375, see:)。
我的假设是:a、b、c没有舍入误差,所以它们的和也应该没有舍入误差,就像
1+2+3 == 3+2+1
0.5+0.375+0.25 == 0.25+0.375+0.5
但是现在a+b+c不是这种情况,我这里的假设有什么问题吗?
总和不准确
您假设精确值之和是精确的是错误的。
浮点运算使用一些固定格式的数字(例如 float
的 24 个二进制数字)。两个 24 位数字的数学和可能有 25 位数字,因此需要四舍五入以在 24 位数字(和指数)内表示。
此外,当两个具有不同指数的数字相加时,一个数字相对于另一个数字偏移。由于偏移量,总和可能有额外的数字,因此必须再次四舍五入。
当您以不同顺序添加数字时,结果四舍五入可能会有所不同。
不精确和的例子
这些示例使用三位二进制有效数字。
在这个例子中,加法进入一个新的列:
1.10 • 23
1.01 • 23
――――――――――
10.11 • 23 Exact sum, too many digits, must be rounded.
11.0 • 23 Sum rounded to three digits.
1.10 • 24 Rounded sum, exponent adjusted to normalize significand.
在此示例中,数字具有不同的指数,对此进行调整会将数字移到新列中:
1.11 • 23
1.01 • 25 Different exponent requires adjustment.
0.0111 • 25 Adjusted to match exponent.
1.01 • 25
――――――――――――
1.1011 • 25 Exact sum, too many digits, must be rounded.
1.11 • 25 Rounded sum.
非关联和的例子
现在我们可以看看以不同的方式将三个数字相加,并看到产生不同的总和。
我们将比较(1.10•20 + 1.10•20) + 1.00•24) 到 1.10•20 + (1.10•20 + 1.00•24).
对于第一个表达式,我们添加第一个和第二个操作数,然后是第三个:
Add first and second operands:
1.10 • 20 First operand.
1.10 • 20 Second operand.
――――――――――
11.00 • 20 Exact sum, too many digits, must be rounded.
11.0 • 20 Rounded sum, must be normalized.
1.10 • 21 Normalized, rounded sum.
Add previous result and third operand:
1.10 • 21 Previous result.
1.00 • 24 Third operand.
Exponents do not match, so adjust and then add:
0.00110 • 24 Previous result adjusted to match exponent.
1.00 • 24 Third operand.
――――――――――――
1.00110 • 24 Exact sum, too many digits, must be rounded.
1.01 • 24 Rounded sum.
对于第二个表达式,我们添加第二个和第三个操作数,然后是第一个:
Add second and third:
1.10 • 20 Second operand.
1.00 • 24 Third operand.
Exponents do not match, so adjust, then add:
0.000110 • 24 Second operand adjusted to match exponent.
1.00 • 24 Third operand.
――――――――――――――
1.000110 • 24 Exact sum, too many digits, must be rounded.
1.00 • 24 Rounded sum.
Add first operand and previous result:
1.10 • 20 First operand.
1.00 • 24 Previous result.
Exponents do not match, so adjust and then add:
0.000110 • 24 First operand adjusted to match exponent.
1.00 • 24 Previous result.
―――――――――――――
1.000110 • 24 Exact sum, too many digits, must be rounded.
1.00 • 24 Rounded sum.
第一个表达式产生 1.01•24,而第二个表达式产生 1.00•24。所以操作数相加的顺序会影响结果。
我的假设是加法的中间值可能有不同的舍入点误差。例如,a + b
可能会产生舍入误差。并且 b + c
可能不会,或者结果值在与最终值相加时可能会导致不同类型的浮点错误。加括号让运算顺序和中间值更清晰:
(a + b) + c
(c + b) + a
假设我有3个浮点数,可以用浮点数准确表示:
var a=0.1000000000000000055511151231257827021181583404541015625;
var b=0.200000000000000011102230246251565404236316680908203125;
var c=0.299999999999999988897769753748434595763683319091796875;
它们是0.1、0.2、0.3实际存储在float中的值。我认为a+b+c应该等于c+b+a,因为a、b和c都已经四舍五入了,而且是精确值,它们的和应该不依赖于顺序,但现在我测试了它不是:
var a=0.1000000000000000055511151231257827021181583404541015625;
var b=0.200000000000000011102230246251565404236316680908203125;
var c=0.299999999999999988897769753748434595763683319091796875;
console.log(a+b+c==c+b+a);
这是什么原因?
注意:我不是在问 Is floating point math broken?, because 0.1000000000000000055511151231257827021181583404541015625 is already the accurate value in floating point number (some float numbers can be represented without rounding errors, for example 0.5,0.75 and 0.375, see:
我的假设是:a、b、c没有舍入误差,所以它们的和也应该没有舍入误差,就像
1+2+3 == 3+2+1
0.5+0.375+0.25 == 0.25+0.375+0.5
但是现在a+b+c不是这种情况,我这里的假设有什么问题吗?
总和不准确
您假设精确值之和是精确的是错误的。
浮点运算使用一些固定格式的数字(例如 float
的 24 个二进制数字)。两个 24 位数字的数学和可能有 25 位数字,因此需要四舍五入以在 24 位数字(和指数)内表示。
此外,当两个具有不同指数的数字相加时,一个数字相对于另一个数字偏移。由于偏移量,总和可能有额外的数字,因此必须再次四舍五入。
当您以不同顺序添加数字时,结果四舍五入可能会有所不同。
不精确和的例子
这些示例使用三位二进制有效数字。
在这个例子中,加法进入一个新的列:
1.10 • 23 1.01 • 23 ―――――――――― 10.11 • 23 Exact sum, too many digits, must be rounded. 11.0 • 23 Sum rounded to three digits. 1.10 • 24 Rounded sum, exponent adjusted to normalize significand.
在此示例中,数字具有不同的指数,对此进行调整会将数字移到新列中:
1.11 • 23 1.01 • 25 Different exponent requires adjustment. 0.0111 • 25 Adjusted to match exponent. 1.01 • 25 ―――――――――――― 1.1011 • 25 Exact sum, too many digits, must be rounded. 1.11 • 25 Rounded sum.
非关联和的例子
现在我们可以看看以不同的方式将三个数字相加,并看到产生不同的总和。
我们将比较(1.10•20 + 1.10•20) + 1.00•24) 到 1.10•20 + (1.10•20 + 1.00•24).
对于第一个表达式,我们添加第一个和第二个操作数,然后是第三个:
Add first and second operands: 1.10 • 20 First operand. 1.10 • 20 Second operand. ―――――――――― 11.00 • 20 Exact sum, too many digits, must be rounded. 11.0 • 20 Rounded sum, must be normalized. 1.10 • 21 Normalized, rounded sum. Add previous result and third operand: 1.10 • 21 Previous result. 1.00 • 24 Third operand. Exponents do not match, so adjust and then add: 0.00110 • 24 Previous result adjusted to match exponent. 1.00 • 24 Third operand. ―――――――――――― 1.00110 • 24 Exact sum, too many digits, must be rounded. 1.01 • 24 Rounded sum.
对于第二个表达式,我们添加第二个和第三个操作数,然后是第一个:
Add second and third: 1.10 • 20 Second operand. 1.00 • 24 Third operand. Exponents do not match, so adjust, then add: 0.000110 • 24 Second operand adjusted to match exponent. 1.00 • 24 Third operand. ―――――――――――――― 1.000110 • 24 Exact sum, too many digits, must be rounded. 1.00 • 24 Rounded sum. Add first operand and previous result: 1.10 • 20 First operand. 1.00 • 24 Previous result. Exponents do not match, so adjust and then add: 0.000110 • 24 First operand adjusted to match exponent. 1.00 • 24 Previous result. ――――――――――――― 1.000110 • 24 Exact sum, too many digits, must be rounded. 1.00 • 24 Rounded sum.
第一个表达式产生 1.01•24,而第二个表达式产生 1.00•24。所以操作数相加的顺序会影响结果。
我的假设是加法的中间值可能有不同的舍入点误差。例如,a + b
可能会产生舍入误差。并且 b + c
可能不会,或者结果值在与最终值相加时可能会导致不同类型的浮点错误。加括号让运算顺序和中间值更清晰:
(a + b) + c
(c + b) + a