假设我有3个浮点数,可以用浮点数精确表示:
var a=0.1000000000000000055511151231257827021181583404541015625;
var b=0.200000000000000011102230246251565404236316680908203125;
var c=0.299999999999999988897769753748434595763683319091796875;
console.log(a+b+c==c+b+a);
它们是实际值0.1,0.2,并且存储在float中的0.3。我认为a + b + c应该等于c + b + a,因为a,b和c都已经四舍五入并且是精确值,它们的总和不应该依赖于顺序,但现在我测试的不是:< / p>
1+2+3 == 3+2+1
0.5+0.375+0.25 == 0.25+0.375+0.5
是什么原因?
注意:我不是在询问Is floating point math broken?,因为0.1000000000000000055511151231257827021181583404541015625已经是浮点数的准确值(某些浮点数可以表示没有舍入错误,例如0.5,0.75和0.375,请参阅:{ {3}})。
我的假设是:a,b和c没有舍入错误,因此它们的总和也应该没有舍入错误,就像
一样repeat
但是现在a + b + c不是这样的,我的假设在这里有什么不对?
答案 0 :(得分:3)
您认为精确值的总和是精确的假设是错误的。
浮点运算使用为格式固定的一些数字位数(例如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•2 0 + 1.10•2 0 )+ 1.00•2 4 )至1.10•2 0 +(1.10•2 0 + 1.00•2 4 )。
对于第一个表达,我们添加第一个和第二个操作数,然后是第三个:
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•2 4 ,而第二个表达式产生1.00•2 4 。因此,添加操作数的顺序会影响结果。
答案 1 :(得分:0)
我的假设是加法的中间值可能有不同的舍入点误差。例如,a + b
可能会导致舍入错误。并且b + c
可能没有,或者在添加最终值时,结果值可能会导致不同类型的浮点错误。添加括号使操作顺序和中间值更清晰:
(a + b) + c
(c + b) + a