简体   繁体   English

文字与计算的浮点精度

[英]Floating point precision in literals vs calculations

I'm wondering why floating point numbers in Java can represent exact value when they are initialized as literals, but they are approximate when they represent result of some calculation. 我想知道为什么Java中的浮点数在初始化为文字时可以表示精确值,但是当它们表示某些计算结果时它们是近似值。 For example: 例如:

double num1 = 0.3;
double num2 = 0.1 + 0.2;
System.out.println(num1);
System.out.println(num2);

why the result is: 为什么结果是:

0.3
0.30000000000000004

and not: 并不是:

0.30000000000000004
0.30000000000000004

When there is no exact binary representation of 0.3. 当没有精确的二进制表示0.3。 I know the BigDecimal class, but I don't quite understand this primitive numbers inconsistency. 我知道BigDecimal类,但我不太明白这个原始数字的不一致性。

None of the three numbers can be represented exactly as a double . 这三个数字中没有一个可以完全表示为double The reason that you get different results is that the value after adding 0.1 to 0.2 has a different representation error than 0.3 . 您得到不同结果的原因是,添加0.10.2之后的值具有与0.3不同的表示误差。 The difference of about 5.5E-17 is enough to cause a difference when printing out the result ( demo ). 大约5.5E-17的差异足以在打印出结果( 演示 )时产生差异。

double a = 0.2;
double b = 0.1;
double c = 0.3;
double d = a+b;
double e = d-c; //  This is 5.551115123125783E-17

When 0.3 is converted to its representation as ones and zeroes then converted back to decimal, it rounds to 0.3. 当0.3被转换为其表示为1和0然后转换回小数时,它将舍入为0.3。 However, when 0.1 and 0.2 are respectively converted to binary, the errors add up upon addition so as to show up when the sum is converted back to decimal. 但是,当0.1和0.2分别转换为二进制时,误差在加法时相加,以便在总和转换回十进制时显示。 A thorough explanation would involve demonstrating the IEEE representation of each number along with the addition and conversions. 详尽的解释将涉及演示每个数字的IEEE表示以及添加和转换。 A bit involved, but I hope you got the idea. 有点涉及,但我希望你有这个想法。

The addition itself cannot produce an exact representation of 0.3 , hence printing the result of 0.1 + 0.2 yields 0.30000000000000004 . 添加本身不能产生0.3的精确表示,因此打印0.1 + 0.2的结果产生0.30000000000000004

On the other hand, when calling System.out.println(0.3); 另一方面,当调用System.out.println(0.3); , the println(double) method will perform some rounding on the result: it eventually calls Double.toString(double) which mentions that the result is approximate: println(double)方法将对结果执行一些舍入:它最终调用Double.toString(double) ,它提到结果是近似的:

How many digits must be printed for the fractional part of m or a? m或a的小数部分必须打印多少位? There must be at least one digit to represent the fractional part, and beyond that as many, but only as many, more digits as are needed to uniquely distinguish the argument value from adjacent values of type double. 必须至少有一个数字来表示小数部分,并且除此之外必须有多个,但只需要多少,更多的数字来唯一地区分参数值和double类型的相邻值。 That is, suppose that x is the exact mathematical value represented by the decimal representation produced by this method for a finite nonzero argument d. 也就是说,假设x是由该方法为有限非零参数d生成的十进制表示所表示的精确数学值。 Then d must be the double value nearest to x; 那么d必须是最接近x的double值; or if two double values are equally close to x, then d must be one of them and the least significant bit of the significand of d must be 0. 或者如果两个double值同样接近x,则d必须是其中之一,d的有效位的最低有效位必须为0。

If you use a BigDecimal the difference can be seen: 如果你使用BigDecimal ,可以看到差异:

System.out.println(0.3);  // 0.3
System.out.println(new BigDecimal(0.3));  // 0.299999999999999988897769753748434595763683319091796875

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM