为什么Chrome在(.1).toString(3)的点后产生1099位数?

时间:2014-09-01 09:31:57

标签: javascript google-chrome floating-point tostring base

我觉得这只是一个无害的错误,但我仍然想了解发生了什么。

当我注意到一个函数在Chrome中返回一个荒谬的长字符串时,我正在玩一些代码来在画布上呈现Peano curve,该画布涉及在基数3中表达逻辑坐标。仔细观察,结果表明

(.1).toString(3)

在Chrome中评估

  

0.00220022002200220022002200220022010000210021000011010100020220112020121211021220201121200010202102221012011200102210101012020202002210201010020021011001000020222100102200220210212210211000201201022020202001100022202200120010210220200011202201010010221121201212202101221212001212001222121001102101022020000120212112002222211011112111220121211112022112100100222121000022102202101222002011202200112101201100111200000110110010101100220121020011020202102112021110010021012001020222211122120120110000221100220200011001122121021021001110002222110122112202001121200021001212100002220022011202201110220211200221011122012200011010121122012110100101101220112011200222101020211000020001210201200011121222222202012002200122111220010220220012220112211002120011000102000012110220211202102221100222212020021200112102200120010221120122021101012121000112200002202001222221022011002021010121102012212022112202011110211121122011201011211222121122202111100020201202010220221212101200022020212120001012222211011222010 01100021211101012101011202020110010112202201201001020212002021112020021121202000000222122210022012001201

如此处所示:http://jsfiddle.net/zvp8osm8/

据我所知,在这种情况下,只有点后面的前33位有意义,其余的看起来像没有可识别模式的随机垃圾。 对于不同的基数和指数,在点之后产生1099(!)个数字的类似结果也类似于(10000000000.1).toString(3)(.7).toString(7)。像(.5).toString(3)这样的其他值也会产生很长的字符串,但数字都有意义。

除Opera之外的其他浏览器在每种情况下只产生合理数量的数字,这让我觉得问题出现在Chrome的Javascript引擎中。

我现在有两个问题:

  • 为什么基础3中的十进制数字表示在Chrome中包含这么多无关紧要的数字?
  • 随机数字可能来自哪里?

3 个答案:

答案 0 :(得分:5)

对于您显示的特定情况,看起来数字是使用以下天真算法生成的,从x = .1开始。

  1. x乘以3。
  2. 提取结果的整数和小数部分。
  3. 将整数部分输出为数字,并将x替换为小数部分。
  4. 重复步骤1至3直到无聊(或直到达到某个预设限制)。
  5. 这在数学上可以很好地工作,但是在浮点世界中这是完全无稽之谈,当然,因为乘以3和后续舍入到最近的浮点数可能会引入一个小错误,并且在30之后数字左右,错误已经完全淹没了原始数字,而我们只是变得垃圾。

    据推测,在初始数字绝对值大于1.0的情况下,还有一些方法可以处理数字之前的数字,但是没有样本输出,我不会去猜猜算法是什么。

    为了证明上述原因,这里有一些Python中的代码,其输出与问题中给出的完全匹配。这里,modf是提取Python float的小数部分和整数部分的操作。

    >>> from math import modf
    >>> x = 0.1
    >>> digits = []
    >>> for _ in xrange(1099):
    ...     x, digit = modf(3.0 * x)
    ...     digits.append(str(int(digit)))
    ... 
    >>> print('0.' + ''.join(digits))
    

    输出:

      

    0.00220022002200220022002200220022010000210021000011010100020220112020121211021220201121200010202102221012011200102210101012020202002210201010020021011001000020222100102200220210212210211000201201022020202001100022202200120010210220200011202201010010221121201212202101221212001212001222121001102101022020000120212112002222211011112111220121211112022112100100222121000022102202101222002011202200112101201100111200000110110010101100220121020011020202102112021110010021012001020222211122120120110000221100220200011001122121021021001110002222110122112202001121200021001212100002220022011202201110220211200221011122012200011010121122012110100101101220112011200222101020211000020001210201200011121222222202012002200122111220010220220012220112211002120011000102000012110220211202102221100222212020021200112102200120010221120122021101012121000112200002202001222221022011002021010121102012212022112202011110211121122011201011211222121122202111100020201202010220221212101200022020212120001012222211011222010 01100021211101012101011202020110010112202201201001020212002021112020021121202000000222122210022012001201

    这应该回答你的一个问题:即随机数字的来源。我无法回答为什么Chrome会选择输出这么多数字的问题。

答案 1 :(得分:2)

首先必须将数字.1转换为浮点数,以二进制表示。在二进制中,.1无法精确表示,因此低位数字中的某个错误将在何处。这类似于尝试用十进制表示1/7:它是重复序列.142857 142857 ...;无论你在哪里结束,都会失去精确度。

当然后将其转换为基数3时,这些数字中的错误会导致您看到的随机性。

答案 2 :(得分:1)

从ECMAScript 5.1规范开始,即15.7.4.2 Number.prototype.toString([radix])9.8.1 ToString Applied to the Number Type

如果基数不是10,则将数字转换为字符串的精确算法取决于实现(参见15.7.4.2),但是,它应该是9.8.1中概述的算法的推广。 。
这意味着每个浏览器(以及其他所有实现)都可以自由选择是否要提供标准精度(最多21位数)或更多。