求助众位前辈,19.99 * 100 - 1999 算式为什么结果是-2.2737367544323E-13 ?

2019-07-22 18:23:49 +08:00
 qq292382270

脑壳疼,测试段代码需要用到个算式 : 19.99 * 100 - 1999,结果为-2.2737367544323E-13;
算式中的 19.99 可以以变量形式传递进来,也还是这个结果. 用 19.98100-1998(或其他两位小数的数字减去它乘 100 的数字) 都正常为 0.
算式在 java php js 中都为这个结果,感觉 19.99 好诡异,各位前辈有遇到过这样的情况么,有没有好的解决方案来避免.目前我的测试是用命令(php) sprintf("%.0f", 19.99
100)才能正常执行..
(咦~好奇怪,上面这段文本突然斜体了...)

3463 次点击
所在节点    程序员
20 条回复
limuyan44
2019-07-22 18:33:11 +08:00
java 中有专门用于高精度计算的类型,php 料想应该也有,通常涉及浮点型本身就是近似计算。
ipwx
2019-07-22 18:36:19 +08:00
月经问题。。。

内存中浮点数是二进制存储的。一个有限十进制小数不一定存在对应的有限二进制小数。事实上除了能写成“某个整数 + sum_{i=1}^k a_i 2^{-i}”这种形式的小数,都不能写成有限的二进制小数。

而计算机的浮点数有长度限制,一个无限循环二进制小数截断以后,就和原来的十进制小数并不完全相等了。你减出来的这个值,就是截断误差。
johnniang
2019-07-22 18:38:01 +08:00
建议看看 《计算机组成原理》。
Rekkles
2019-07-22 18:38:39 +08:00
php 请用 bcmath
echo bcsubbcmul(19.99,100,2),1999);
Raymon111111
2019-07-22 18:59:56 +08:00
简单讲

你看见的 19.99 十进制下人畜无害, 有可能在二进制下是一个无限循环小数 (可以自己做一下进制转换)

这样一来, 就算乘以 100 和后面的数相减也得不到 0
ClericPy
2019-07-22 19:15:33 +08:00
搜一下 浮点数 IEEE
laodao1990
2019-07-22 19:24:25 +08:00
推荐看看《深入理解计算机系统》
通俗点解释就是,计算机是用二进制表示的,对于某些小数和人类用 10 进制无法准确表示 1/3 是一个意思,会有误差。
VDimos
2019-07-22 19:31:01 +08:00
真—月经问题
Kirscheis
2019-07-22 19:40:06 +08:00
f64(19.99) = 0x1.3fd70a3d70a3dp+4

f64(100.0) = 0x1.9000000000000p+6

0x1.3fd70a3d70a3dp+4 * 0x1.9000000000000p+6 = 0x1.f3bffffffffffp+10

f64(1999.0) = 0x1.f3c0000000000p+10

所以结果是

0x1.f3c0000000000p+10 - 0x1.f3bffffffffffp+10 = -0x1.0000000000000p-42

转换到 10 进制就是 -2.2737367544323206e-13
Kirscheis
2019-07-22 19:41:34 +08:00
上面打多一个负号,凑合看
joshu
2019-07-22 19:42:01 +08:00
浮点数的比较一般是小于 1e-N ( N 多少我忘了)就视为相等
不要比较浮点数相等
akazure
2019-07-22 19:42:40 +08:00
一般采取新建一个 equal 方法,并定义一个极小量,以作为误差范围。
jamesliu96
2019-07-22 19:43:11 +08:00
IEEE 754
Mistwave
2019-07-22 19:48:20 +08:00
littlewing
2019-07-22 19:50:19 +08:00
手动搜索 “ IEEE754 浮点数”
heart4lor
2019-07-22 20:29:37 +08:00
浮点数存储问题都不知道的么,斜体是因为 markdown 语法
xuanbg
2019-07-22 20:41:34 +08:00
一般的业务场景有小数的时候不要用浮点数,不要用浮点数,不要用浮点数。重要的事情说 3 遍。因为我们默认计算的结果是精确的,而浮点数是有精度问题的,不能满足精确的要求。
msg7086
2019-07-22 23:27:42 +08:00
小数通常是近似值不是精确值。
TobiahShaw
2019-07-23 09:48:37 +08:00
因为不连续,你可以判断 19.99 * 100 - 1999 < Epsilon,或者使用 BigDecimal
brust
2019-07-23 14:27:28 +08:00
精度问题 浮点数 计算会丢失精度
java 的话推荐<码出高效>这本书

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

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

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

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

© 2021 V2EX