20

考虑以下:

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(!)实际上是如何做到的正确的就像人类一样:

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)...:直到我们非常接近于零,toFixed在 IE 中显然试图偷工减料。

免责声明:我知道浮点数学有点缺陷。我不明白的是 IE 和浏览器世界的其他部分有什么区别。

4

3 回答 3

8

我感谢 Eric 的贡献,但恕我直言,它并没有回答问题。我承认我对那些“正确”和“非常正确”的短语太半开玩笑了。但是,是的,我知道 IE 行为实际上是一种偏差。

反正。我仍在寻找导致 IE 行为不同的解释——我终于得到了一些看起来像线索的东西......具有讽刺意味的是,在 Mozilla 的跟踪器中,在这个冗长的讨论中。引用:

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”存储为值。该规范没有明确存储格式。因此,当 IEa.toFixed开始时,它以精确的字符串表示开始,而 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"
于 2013-10-03T22:01:47.413 回答
8

报告的行为偏离了ECMA 规范的要求。

根据第 8.5 节,该Number类型具有 IEEE-754 64 位二进制值,但只有一个 NaN。所以 2.175 不能精确表示;你能得到的最接近的是 2.17499999999999982236431605997495353221893310546875。

根据 15.7.4.5,toFixed(20)使用的算法归结为:

  • “设n是一个整数,n ÷ 10 fx的精确数学值尽可能接近于零。如果有两个这样的n,请选择较大的n。”</li>
  • 上面,f是20(请求的位数),x是操作数,应该是2.17499999999999982236431605997495353221893310546875。
  • 这导致为n选择 217499999999999982236 。
  • 然后n被格式化,产生“2.17499999999999982236”。
于 2013-10-03T18:29:02.420 回答
1

首先,浮点数不能用二进制数“精确”表示。会有一个升高/降低,值会高一点低一点。升高/降低多少取决于转换的完成方式。即使对于ECMAScripttoFixed().

但是 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 处理转换“不仅不同”而且“在不同的场合”。

Number在 JavaScript 中,当我们创建一个数字(也就是一个有或没有new关键字的类的实例)时,我们实际上提供了一个literal. 文字总是一个字符串,即使它表示一个数字[1]。浏览器解析文字并创建对象并分配表示的值。

现在,这里的事情往往以不同的方式发展。IE 推迟转换,直到需要它。这意味着在操作发生之前,IE 将数字保持为文字(或某种中间格式)。但是 Chrome 会立即将其转换为操作格式。

操作完成后,IE 不会恢复到文字格式或中间格式,因为它没有意义,可能会导致精度略有损失。

我希望能澄清一些事情。


[1]代码中表示的值始终为literals。如果你引用它们,它们被称为String Literals。

于 2013-10-10T17:57:22.687 回答