javascript 如何以如此准确的方式打印 0.1? [英] How does javascript print 0.1 with such accuracy?

查看:28
本文介绍了javascript 如何以如此准确的方式打印 0.1?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

听说 javascript Numbers IEEE 754 浮点数,这解释了原因

<代码>>0.3 - 0.20.09999999999999998

但我不明白

<代码>>0.10.1

我认为 0.1 不能准确地存储为以 2 为底的浮点数,但它会立即打印出来,就像它一直是 0.1 一样.是什么赋予了?解释器在打印之前是否进行了一些舍入?

至少有 2 个版本的 IEEE 754 对我没有帮助:1984 版和 2008.听起来后者添加了对十进制算术的全面支持.好像我们没有.

解决方案

JavaScript 使用 IEEE-754 双精度数(binary64"正如 2008 规范所说的那样;也就是说,正如您所怀疑的,它是 base 2 版本,不是 2008 base 10 版本).

你得到字符串 "0.1" 的原因是数值 0.1,即使 0.1 不能完美地用 binary64 表示,是吗 —

TL;DR:字符串不是数字的精确版本,它只是足以将其与其相邻的不完全精确的数字区分开来

— 该规范定义了将数字转换为字符串的复杂规则,以解决精度不足的问题.它们包含在 §9.8.1 - ToString应用于数字类型:

<块引用>

  1. 如果 mNaN,则返回字符串 "NaN".
  2. 如果 m+0-0,则返回字符串 "0".
  3. 如果 m 小于零,则返回字符串 "-" 和 ToString(-m) 的字符串连接.
  4. 如果 m 是无穷大,则返回字符串 "Infinity".
  5. 否则,令nks为整数,使得k≥1, 10k−1 ≤ s <10k,s × 10n−k的Number值为mk小到可能的.请注意,ks 的十进制表示中的位数,s 不能被 10 整除,并且最低有效位s 的数量不一定由这些标准唯一确定.
  6. 如果kn ≤ 21,则返回s十进制表示的k位组成的Stringem>(按顺序,没有前导零),后跟 n-k 个字符 '0'.
  7. 如果 0 s的十进制表示的最高有效n位组成的String,后跟一个小数点'.',后跟 s 的十进制表示的剩余 k−n 位.
  8. 如果 -6 <n ≤ 0,返回由字符'0'组成的String,后跟一个小数点'.',后跟-n次出现字符'0',后跟s的十进制表示的k位.
  9. 否则,如果k = 1,则返回由s个位数字后跟小写字符'e'组成的String,后跟加号'+'或减号'-'根据n-1是正还是负,后跟小数整数 abs(n-1) 的表示(没有前导零).
  10. 返回由s的十进制表示的最高有效位组成的字符串,后跟一个小数点'.',然后是剩余的ks的十进制表示的-1位,后跟小写字符'e',后跟加号'+'或减号'-'根据n-1是正数还是负数,后跟整数abs(n−1)(没有前导零).

然后有以下注释;点击链接了解完整详情.注 3 可能最相关:

<块引用>

注意 3

ECMAScript 的实施者可能会发现 David M. Gay 编写的用于浮点数二进制到十进制转换的论文和代码很有用:

Gay, David M. 正确舍入二进制-十进制和十进制-二进制转换.数值分析,手稿 90-10.AT&T 贝尔实验室(新泽西州默里山).1990 年 11 月 30 日.http://cm.bell-labs.com/cm/cs/doc/90/4-10.ps.gz.相关代码可用作http://cm.bell-labs.com/netlib/fp/dtoa.c.gzhttp://cm.bell-labs.com/netlib/fp/g_fmt.c.gz 也可以在各种 netlib 镜像站点找到.

对我来说,4-10.ps.gz 文件似乎已损坏(无法阅读第 6-8 页),但我在这里找到了 PDF:http://ampl.com/REFS/rounding.pdf (不像看起来那样随机链接,显然 AMPL 是本文工作的主要动机).

I've heard that javascript Numbers are IEEE 754 floating points, which explains why

> 0.3 - 0.2
0.09999999999999998

but I don't understand

> 0.1
0.1

I thought 0.1 couldn't be accurately stored as a base 2 floating point, but it prints right back out, like it's been 0.1 all along. What gives? Is the interpreter doing some rounding before it prints?

It's not helping me that there are at least 2 versions of IEEE 754: 1984 edition and 2008. It sounds like the latter added full support for decimal arithmetic. Doesn't seem like we have that.

解决方案

JavaScript uses IEEE-754 double-precision numbers ("binary64" as the 2008 spec puts it; that is, as you suspected, it's the base 2 version, not the 2008 base 10 version).

The reason you get the string "0.1" for the number value 0.1, even though 0.1 can't be perfectly represented in binary64, is that —

TL;DR: the string isn't an exact version of the number, it's just exact enough to differentiate it from its neighboring not-quite-exact numbers

— the specification defines complex rules for converting numbers to strings in order to address that lack of precision. They're covered in §9.8.1 - ToString Applied to the Number Type:

  1. If m is NaN, return the String "NaN".
  2. If m is +0 or −0, return the String "0".
  3. If m is less than zero, return the String concatenation of the String "-" and ToString(−m).
  4. If m is infinity, return the String "Infinity".
  5. Otherwise, let n, k, and s be integers such that k ≥ 1, 10k−1 ≤ s < 10k, the Number value for s × 10n−k is m, and k is as small as possible. Note that k is the number of digits in the decimal representation of s, that s is not divisible by 10, and that the least significant digit of s is not necessarily uniquely determined by these criteria.
  6. If kn ≤ 21, return the String consisting of the k digits of the decimal representation of s (in order, with no leading zeroes), followed by n−k occurrences of the character ‘0’.
  7. If 0 < n ≤ 21, return the String consisting of the most significant n digits of the decimal representation of s, followed by a decimal point ‘.’, followed by the remaining k−n digits of the decimal representation of s.
  8. If −6 < n ≤ 0, return the String consisting of the character ‘0’, followed by a decimal point ‘.’, followed by −n occurrences of the character ‘0’, followed by the k digits of the decimal representation of s.
  9. Otherwise, if k = 1, return the String consisting of the single digit of s, followed by lowercase character ‘e’, followed by a plus sign ‘+’ or minus sign ‘−’ according to whether n−1 is positive or negative, followed by the decimal representation of the integer abs(n−1) (with no leading zeroes).
  10. Return the String consisting of the most significant digit of the decimal representation of s, followed by a decimal point ‘.’, followed by the remaining k−1 digits of the decimal representation of s, followed by the lowercase character ‘e’, followed by a plus sign ‘+’ or minus sign ‘−’ according to whether n−1 is positive or negative, followed by the decimal representation of the integer abs(n−1) (with no leading zeroes).

Then there are following notes; follow the link for the full details. Note 3 is probably most relevant:

NOTE 3

Implementers of ECMAScript may find useful the paper and code written by David M. Gay for binary-to-decimal conversion of floating-point numbers:

Gay, David M. Correctly Rounded Binary-Decimal and Decimal-Binary Conversions. Numerical Analysis, Manuscript 90-10. AT&T Bell Laboratories (Murray Hill, New Jersey). November 30, 1990. Available as http://cm.bell-labs.com/cm/cs/doc/90/4-10.ps.gz. Associated code available as http://cm.bell-labs.com/netlib/fp/dtoa.c.gz and as http://cm.bell-labs.com/netlib/fp/g_fmt.c.gz and may also be found at the various netlib mirror sites.

For me, the 4-10.ps.gz file seemed to be corrupted (couldn't read pages 6-8), but I found a PDF here: http://ampl.com/REFS/rounding.pdf (not as random a link as it may seem, apparently AMPL was a prime motivation for the work in the paper).

这篇关于javascript 如何以如此准确的方式打印 0.1?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆