Javascript Number.prototype.toFixed:在Internet Explorer中非常正确

Javascript Number.prototype.toFixed:在Internet Explorer中非常正确,javascript,internet-explorer,floating-point,Javascript,Internet Explorer,Floating Point,考虑以下几点: var x = 2.175; console.log(x.toFixed(2)); // 2.17 什么?不,这并不奇怪。这是相当明显的,请参见:数字文字2.175实际上是作为一个比2.175小一点点的值存储在内存中(根据IEEE-754规则)。这很容易证明: console.log(x.toFixed(20)); // 2.17499999999999982236 这就是它在32位Windows安装程序上最新版本的Firefox、Chrome和Opera中的工作原理。但这

考虑以下几点:

var x = 2.175;
console.log(x.toFixed(2));  // 2.17
什么?不,这并不奇怪。这是相当明显的,请参见:数字文字
2.175
实际上是作为一个比2.175小一点点的值存储在内存中(根据IEEE-754规则)。这很容易证明:

console.log(x.toFixed(20)); // 2.17499999999999982236
这就是它在32位Windows安装程序上最新版本的Firefox、Chrome和Opera中的工作原理。但这不是问题所在

真正的问题是,Internet Explorer如何像人类一样做到正确:

var x = 2.175;
console.log(x.toFixed(2));  // 2.18
console.log(x.toFixed(20)); // 2.17500000000000000000
好吧,我太激动了:事实上,我测试过的所有Internet Explorer(IE8-11,甚至MS Edge!)的表现都是一样的。还有,沃特

更新:它变得更奇怪了:

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非常相似;虽然(x-=0.01).
,顺便说一句:在我们非常接近于零之前,IE中的
toFixed
显然是想抄近路


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

报告的行为偏离了浏览器的要求

根据第8.5条,除仅有一个NaN外,
编号
类型具有IEEE-754 64位二进制值。因此,2.175不能准确表示;最接近的是2.1749999999982236431605997495353221893310546875

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

  • 设n是一个整数,其精确数学值n÷10f–x尽可能接近于零。如果有两个这样的n,则选择较大的n
  • 在上面的例子中,f是20(请求的位数),x是操作数,应该是2.17499999999822364316059974953221893310546875
  • 这将导致为n选择2174999999982236
  • 然后n被格式化,生成“2.1749999999982236”

我感谢Eric的贡献,但恕我直言,这并不能回答问题。我承认我对那些“正确的”和“惊人地正确的”的短语过于厚颜无耻;但是是的,我知道IE的行为实际上是一种偏差

无论如何。我仍然在寻找导致IE行为不同的原因的解释——最终我得到了一些看起来像线索的东西。。。讽刺的是,在Mozilla的追踪器中,在这个。引述:

IE和Mozilla的区别如下IE正在将“a”存储为字符串 Mozilla将“a”存储为一个值。规范没有明确说明问题 存储格式。因此,当IE执行
a.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"

首先,浮点不能用二进制数“精确”表示。将出现一个上升/下降,该值将稍高稍低。提升/降低的程度取决于转换的方式。即使对于ECMAScript的
toFixed()
的字符串输出,也没有确切的“右值”

但是ECMA标准确实通过er使事情变得有趣。。制定标准。在我看来这是件好事。就像“如果我们都会犯错误,那就让我们犯同样的错误。”

因此,现在的问题是,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)也是如此。当我们执行一个操作(例如加法)时,非常不显著的错误可以加起来变成一个大小稍有不同的错误

现在,由于两个原因,不同浏览器中表示值的差异可能会受到影响:

  • 使用的转换算法
  • 当转换发生时
现在我们不知道algo IE使用了什么,因为我无法查看它的来源。但是上面的测试用例清楚地证明了另一件事,IE和Chrome处理转换的方式“不仅不同”,而且“在不同的场合也不同”

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

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

操作完成后,IE不会恢复为文字格式或中间格式,因为这是毫无意义的,可能会导致精度略有下降

我希望这能澄清一些事情



[1] 代码中表示的值总是
literal
s。如果你引用它们,它们被称为
stringliteral
s.

嗯,IE有自己的JavaScriptengine@StephenThomas:Chrome、Firefox和Opera也有自己的JavaScript引擎。也许IE使用
toString
实现
toFixed
?检查…@DCoder并不容易,这正是我问的原因。)关于
toFixed
b,这里有很多类似的问题
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