1、 System.out.println(0.1); 输出是 0.1
2、 System.out.println(1-0.9); 输出是 0.09999999999999998
为什么同样是 0.1,直接打印就 ok,经过计算后就会由于 double 的精度导致输出循环。
还有,
double x = 1.0;
double y = 3.0;
double z = 3.0;
System.out.println(x/y*z);
为什么输出的是 1 ?应该是 0.9999999999999999999 的循环才对吧。
1
mm163 2018-10-14 10:19:07 +08:00
System.out.println((double)1-0.9)
|
2
ShuoHui 2018-10-14 10:19:18 +08:00 via iPhone 1
蜜汁节点,计算机二进制不能精确表示吧
|
3
zwh2698 2018-10-14 10:31:54 +08:00 via Android
有本书叫计算机组成与原理
|
4
Linyvhan 2018-10-14 10:33:02 +08:00
IEEE754
|
5
johnniang 2018-10-14 10:34:22 +08:00 via Android
说不定被编译器优化了
|
6
FrankFang128 2018-10-14 10:35:50 +08:00
你不能用十进制来思考这个问题
|
7
jiang1234321 OP @mm163 没啥区别啊,本身就会向高精度类型转换的
|
8
jiang1234321 OP @Linyvhan 这个知道,就是不知道为什么同样的 0.1,一个可以准确打印,一个不行,还有就是 0.333333333333333*3 怎么就等于 1 了
|
9
jiang1234321 OP @johnniang 能不能具体一点,或者给个文章什么的看一下,我觉得也是被编译器优化了,
|
10
will0404 2018-10-14 10:47:58 +08:00 via iPhone 1
同楼上,不能用十进制来思考计算机的基本运算。你需要补一下计算机基础。
就答最后一个问题,乘除操作在二进制层面是用移位和简单的加减实现的,不会有精度丢失(除非溢出),而精度是在转为十进制的时候丢失的,1.0/3.0*3.0 是先计算得到了 1.0 的二进制形式,它是可以准确用十进制表示的,当然不会得到 0.99999 … |
11
ech0x 2018-10-14 10:48:22 +08:00 via iPhone
https://zh.wikipedia.org/zh-hans/浮点数
如果你真的要涉及到浮点数的输入输出,和不能有错的浮点数运算的话,请用 BigDecimal |
12
will0404 2018-10-14 10:49:14 +08:00 via iPhone
建议你,CSAPP 第二章读一下。
|
13
liuminghao233 2018-10-14 10:51:04 +08:00 via iPhone
看 csapp 吧
|
14
SuperMild 2018-10-14 10:53:33 +08:00
建议趁此机会练习一下使用搜索引擎,这个问题网上有很多资料,从浅到深都有了,V 站上也是月经。
|
15
icyalala 2018-10-14 10:55:03 +08:00 1
可以仔细看一下 IEEE754 浮点数标准与计算过程。精度损失主要集中在这几个地方:
1. 大部分十进制表示的小数,是不能完全精确的由二进制的 IEEE 浮点数表示出来,编辑器解析字符串的过程中会有 rouding 过程。 2. 打印的时候,二进制大部分情况也不会完全用十进制表示出来,printf 会做舍入和截断。 3. 浮点数计算的过程中,可能会有精度损失。 可以在 https://www.exploringbinary.com/floating-point-converter/ 转换来看看。 "0.1" 解析后是 0x1.999999999999ap-4 "0.9" 解析后是 0x1.ccccccccccccdp-1 (注意最后一位) 1-0.9 得到的结果是 0x1.9999999999998p-4 (注意最后一位) 1.0 / 3.0 * 3.0 这三个数都能精确表示为 IEEE 浮点数 1.0 / 3.0 得到结果 0x1.5555555555555p-2 再乘以 3.0 得到结果 0x1p0 |
17
pipapa 2018-10-14 12:12:19 +08:00 via Android
《深入理解计算机系统》第二章,自己转成二进制手动计算一下就知道了
|
18
VDimos 2018-10-14 13:44:05 +08:00 via Android
浮点的问题就和怎么退出 vim 一样,让无数新手疑惑
|
19
otakustay 2018-10-14 14:01:21 +08:00
|
20
icyalala 2018-10-14 14:25:12 +08:00
@CEBBCAT https://www.exploringbinary.com/topics/ 见 "Correctly Rounded Decimal to Floating-Point Conversion" 这部分的文章。
CSAPP 可以作为基础知识来了解一下,精度究竟是如何损失的、损失结果是怎么样的,这部分就没有介绍了。 把字符串解析为 IEEE754 浮点数,是非常复杂的事情,vc、gcc、jdk 甚至 libc 的 strtod 都出现过 bug。 IEEE754 浮点数运算,又是另一个话题了。 |
22
huiyifyj 2018-10-14 15:45:48 +08:00 via Android
计算机组成与原理,IEEE754 规格化和浮点数运算这方面请认真读。
|
23
pythonee 2018-10-14 19:06:23 +08:00
好久没有在 V2EX 看到这类的技术问题了
|
24
limbo0 2018-10-15 01:16:05 +08:00 via Android
经典问题哈,就是 2 进制保存小数不像整数一样,记得是 x/2**n 这种除以 2 次幂的形式,会有精度问题,运算时会损失精度,就是你看到的样子
|
25
jiang1234321 OP @icyalala 万分感谢,还有问题就是,解析后是这些数字在内存当中实际存储的值吗?
那为什么一个数字直接打印没问题,计算得到的打印就会循环呢?就像是问题描述的那样。 |
26
wutiantong 2018-10-15 10:57:12 +08:00
@Linyvhan #4 已经给出了最本质的解答:IEEE754
@jiang1234321 楼主看了一眼后回复了一句“这个我知道”就选择无视继续反复他的“疑问”。 事实上,假如楼主真的知道 IEEE754 在讲什么,他就不应该还有这些疑问。 所以我建议楼主早日克服自己不求甚解的毛病。 附上 wiki page: https://en.wikipedia.org/wiki/IEEE_754 |
27
icyalala 2018-10-15 11:40:33 +08:00
@jiang1234321
0.1 和 0.9 用二进制表示的话,都是无限循环的,但内存中的 double 只有有限位数,所以要做截断并 rounding 到最近的一个二进制,这里损失了精度。 "0.1" 解析后是 0x1.999999999999ap-4 "0.9" 解析后是 0x1.ccccccccccccdp-1 "1" 解析后是 0x1p0 1-0.9 实际在内存中的计算是 0x1p0 - 0x1.ccccccccccccdp-1 = 0x1.9999999999998p-4 直接写的 0.1 和经过计算得到的 0.1 在内存中的数值不相同,相差 2 ulp。 0x1.999999999999ap-4 转换为十进制时,是 0.1000000000000000055511151231257827021181583404541015625,print 会截断到 0.10000000000000000,舍去结尾的 0,就是 0.1。 0x1.9999999999998p-4 转换为十进制时,是 0.09999999999999997779553950749686919152736663818359375,print 会截断到 0.09999999999999998。 上面的这些 0x 开头的数值是用 Hexadecimal 格式写的,等同于 double 在内存中的实际数据。 |
28
jiang1234321 OP @icyalala 感谢,解答了心中的疑惑。
|
29
jiang1234321 OP @wutiantong 实在是没有这个能力和时间去看一篇英文的文章了,不过还是要感谢你的不求甚解的警钟。
|