为毛 JS 的浮点这么鬼畜的啊

2016-08-01 18:08:13 +08:00
 outlaws

var a = 0.05
a += 0.01

这时候 a 会等于 0.060000000000000000000005

6759 次点击
所在节点    JavaScript
45 条回复
zhanglintc
2016-08-01 20:52:57 +08:00
@iVanilla PHP 不是很熟哦. 不过你一说我突然想起来, 当时 PHP 这段我把我看笑了. PHP 居然如此机智的避开了这个问题.
russj
2016-08-01 20:55:30 +08:00
心疼 js
11138
2016-08-01 20:55:31 +08:00
@iVanilla 是的,这个 precision 默认值在 php.ini 里。
ljcarsenal
2016-08-01 21:09:29 +08:00
不如认真读一下 csapp
y
2016-08-01 21:15:28 +08:00
0.01 和 0.05 都是二进制下的无限循环小数,没法用 64 bit 表示
所以只能作为双精度浮点数存在

0.01 表示为 0x3f847ae147ae147b, 也就是二进制的 1.0100011110101110000101000111101011100001010001111011 * 2^-7

0.05 表示为 0x3fa999999999999a, 也就是二进制的 1.1001100110011001100110011001100110011001100110011010 * 2^-5

用竖式加起来

1.111010111000010100011110101110000101000111101011100011 * 2^-5

注意这个结果已经比原来的小数长了两位(末尾的 11 ),现在要做四舍五入

0.06 是
1.1110101110000101000111101011100001010001111010111000 * 2^-5


0.060000000000000005 是
1.1110101110000101000111101011100001010001111010111001 * 2^-5

显然后者的误差更小。

最后吐槽一下楼主的态度:
实际答案明明是 0.060000000000000005 ,
楼主原帖多了六个 0 ,复制粘贴都不会吗,一定要手打?

(我一定是知乎玩多了...)
kideny
2016-08-01 21:20:43 +08:00
楼主这被打脸的不清啊。
iVanilla
2016-08-01 21:37:38 +08:00
@zhanglintc 我开始以为之前很多人在 bugs.php.ne 反馈了,然后官方把这个值改了,不过我去 https://secure.php.net/releases/ 下载了十多年前的 PHP4.3.11 和 PHP5.0.0 ,发现这个值都是 14 。
@11138 很奇怪为什么你那个例子在不改 precision 的情况下能复现。
hasbug
2016-08-01 21:38:46 +08:00
IEEE 754···
11138
2016-08-01 21:56:23 +08:00
@iVanilla 设置 precision 影响显示的值的位数(整数部分和小数点部分),但是开始和结尾部分的 0 不算数。
<?php
$num = 0.012345600000000000;
ini_set("precision", "12");
echo $num; // 0.0123456
echo "\n";
ini_set("precision", "3");
echo $num; // 0.0123
echo "\n";
ini_set("precision", "5");
echo $num; // 0.012346
echo "\n";
?>
y
2016-08-01 21:58:49 +08:00
@iVanilla 顺便说一句,如果有人要开发一套类似于支付宝的系统(或者 V2EX 的货币系统),那货币的金额一定要是 “ 1 分钱” 的整数倍,而不是有 “ 0.01 元” 这样的数据。所有的数据都要用整型表示,显示的时候再加上小数点,否则时间长了算账的时候会疯掉的。

举个例子,消失的一元钱:

>>> 9999999999999999.0 + 1 - 9999999999999999
0.0
maomaomao001
2016-08-01 22:16:29 +08:00
@kideny 什么叫被大脸????
他不是在好好的问问题吗????
br00k
2016-08-01 22:19:07 +08:00
iVanilla
2016-08-01 22:20:29 +08:00
@br00k 我前面的回复有提到鸟哥这篇文章的 url 。
em2046
2016-08-01 22:30:23 +08:00
@y 何不用定点数 decimal BigDecimal
gilgamesh
2016-08-01 22:44:09 +08:00
ychongsaytc
2016-08-01 23:12:44 +08:00
「你看似有穷的小数, 在计算机的二进制表示里却是无穷的」
---from http://www.laruence.com/2013/03/26/2884.html
kzzhr
2016-08-01 23:27:27 +08:00
相当一部分语言算不好 0.1+0.2-0.3 我记得
jeffersonpig
2016-08-02 08:51:00 +08:00
LZ 大一?
FrankHB
2016-08-02 10:03:49 +08:00
@lovedebug 你数一下 0 的个数大概就不能指望是 IEEE-754 的双精度了,起码是扩展双精度。
@iVanilla PHP 还真有相关的 bug ,而且曾经造成了比较严重的后果,不过具体表现和系统相关: https://bugs.php.net/53632
lovedebug
2016-08-02 11:10:06 +08:00
@FrankHB 双精度是说 JS 用 64 位 IEEE754 存储浮点数

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/296409

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX