请考虑以下事项:
var x = 2.175;
console.log(x.toFixed(2)); // 2.17
什么?不,这并不奇怪。这是相当明显的,请参阅:数字文字2.175
实际上存储在内存中(通过IEEE-754规则),其值仅略小于2.175。这很容易证明:
console.log(x.toFixed(20)); // 2.17499999999999982236
这就是在32位Windows设置的最新版本的Firefox,Chrome和Opera中的工作原理。但那不是问题。
真正的问题是Internet Explorer 6 (!)实际上是如何像人类一样设法做到 right :
var x = 2.175;
console.log(x.toFixed(2)); // 2.18
console.log(x.toFixed(20)); // 2.17500000000000000000
好的,我过度使用了:实际上我测试过的所有Internet Explorer(IE8-11,甚至是MS Edge!)的行为都是一样的。还是,WAT?
更新:陌生人:
x=1.0;while((x-=0.1) > 0) console.log(x.toFixed(20));
IE Chrome
0.90000000000000000000 0.90000000000000002220
0.80000000000000000000 0.80000000000000004441
0.70000000000000010000 0.70000000000000006661
0.60000000000000010000 0.60000000000000008882
0.50000000000000010000 0.50000000000000011102
0.40000000000000013000 0.40000000000000013323
0.30000000000000015000 0.30000000000000015543
0.20000000000000015000 0.20000000000000014988
0.10000000000000014000 0.10000000000000014433
0.00000000000000013878 0.00000000000000013878
为什么差异 - 除了最后一个?为什么最后一个没有区别?顺便说一句,它与x=0.1; while(x-=0.01)...
非常相似:直到我们非常接近零,IE中的toFixed
显然试图削减一些角落。
免责声明:我do知道浮点数学有点瑕疵。我不明白的是IE和浏览器世界的其他部分有什么不同。
答案 0 :(得分:8)
报告的行为偏离了ECMA specification的要求。
根据第8.5节,Number
类型具有IEEE-754 64位二进制值,除了只有一个NaN。所以2.175无法准确表示;你最接近的是2.17499999999999982236431605997495353221893310546875。
根据15.7.4.5,toFixed(20)
使用的算法归结为:
答案 1 :(得分:8)
我很欣赏埃里克的贡献,但是,在充分尊重的情况下,它没有回答这个问题。我承认我对那些“正确”和“令人惊讶的正确”短语过于厚颜无耻;但是,我明白IE行为实际上是一种偏差。
反正。我还在寻找一个解释是什么原因导致IE表现出不同的行为 - 我终于看到了一些线索......具有讽刺意味的是,在Mozilla的跟踪器中,在lengthy discussion中。引用:
OUTPUT IN MOZILLA:
a = 0.827 ==> a.toFixed(17) = 0.82699999999999996
b = 1.827 ==> b.toFixed(17) = 1.82699999999999996
OUTPUT IN IE6:
a = 0.827 ==> a.toFixed(17) = 0.82700000000000000
b = 1.827 ==> b.toFixed(17) = 1.82700000000000000
在IE和Mozilla中看到的差异如下。 IE将'a'存储为字符串 并且Mozilla将'a'存储为值。该规范没有确定 存储格式。因此,当IE执行
a.toFixed
时,它以a开头 确切的字符串表示,而Mozilla遭受往返转换。
对此有一些正式的确认会很好,但至少这解释了我所看到的一切。特别是,
console.log( 0.3.toFixed(20) ); // 0.30000000000000000000
console.log( 0.2.toFixed(20) ); // 0.20000000000000000000
console.log( (0.3 - 0.2).toFixed(20) ); // "0.09999999999999998000"
答案 2 :(得分:0)
首先,浮点数不能以二进制数表示“精确”。会有一个升高/降低,要么值稍高或略低。升高/降低多少取决于转换是如何完成的。即使对于ECMAScript的toFixed()
的字符串输出,也没有“正确的值”。
但ECMA标准确实通过设定标准来提高效率。在我看来,这是一件好事。这就像“如果我们都会犯错误,那就让我们做同样的事。”
所以,现在的问题是,IE如何以及为何偏离标准。我们来看看以下测试用例。
候选人是IE 10.0.9200.16688和Chrome 30.0.1599.69,在x64 Windows 8 Pro上运行。
Case Code IE (10) Chrome (30)
--------------------------------------------------------------------------------
A (0.06).toFixed(20) 0.60000000000000000000 0.05999999999999999778
B (0.05+0.01).toFixed(20) 0.06000000000000000500 0.06000000000000000472
因此,无论是IE还是Chrome,我们都会看到(0.06)
与(0.05+0.01)
不完全相同。这是为什么?这是因为(0.06)的表示非常接近但不等于(0.06),因此(0.05)和(0.01)。当我们执行添加操作时,非常不太重要的错误可以总结为幅度略有不同的错误。
现在,由于两个原因,不同浏览器中代表值的差异可能会受到影响:
现在我们不知道IE使用什么算法,因为我无法查看它的来源。但上述测试用例清楚地展示了另一件事,IE和Chrome处理转换“不仅不同”而且“在不同场合”。
在JavaScript中,当我们创建一个数字(也就是Number
类的实例,包含或不包含new
关键字)时,我们实际上提供了literal
。即使文字表示数字 [1] ,文字仍然是一个字符串。浏览器解析文字并创建对象并分配表示的值。
现在,事情往往会有不同的方式。 IE阻止了转换,直到需要它为止。这意味着在操作发生之前,IE将数字保持为文字(或某种中间格式)。但Chrome会以操作格式立即转换它。
操作完成后,IE不会恢复为文字格式或中间格式,因为它没有意义,可能会导致精度略有下降。
我希望澄清一些事情。
[1] 代码中表示的值始终为literal
s。如果引用它们,则称为String Literal
s。