英文:
How to understand this strange behavior of double in Java?
问题
-
"num" 的值是否为 0.3(精确)?
-
如果 "num" 的值是 0.3,那么为什么 "result2 == compared" 为假?
-
如果 "num" 的值不是 0.3,那么为什么它的字符串输出是 "0.3"?根据我所知,二进制浮点数在十进制中总是可以精确表示的。所以,如果 "num" 的值不是 0.3,它的字符串输出不应该是 "0.3",而应该是类似于 "0.300000000001" 的东西。
-
如果 "num" 的值不是 0.3,那么为什么 "result1 == compared" 为真?
-
如果 "num" 的值不是 0.3,那么它是什么?
英文:
I am trying to understand floating-point numbers more deeply. I know that binary (base 2) floating-point numbers cannot represent some decimal numbers exactly. But I am confused in this strange behavior of double in Java (in Java, double is a binary floating-point number data type).
I write this piece of Java code:
public void myTestMethod() {
double num;
double factor;
double compared;
double result1;
double result2;
String output;
num = 0.3;
factor = 10;
compared = 3;
//num * 10
result1 = num * factor;
//add 10 num, which is mathematically equal to num * 10
result2 = num + num + num + num + num + num + num + num + num + num;
output = "num: " + num + "\n";
output = output + "result1: " + result1 + "\n";
output = output + "result2: " + result2 + "\n";
if (result1 == compared) {
output = output + "result1 == compared\n";
} else {
output = output + "result1 != compared\n";
}
if (result2 == compared) {
output = output + "result2 == compared\n";
} else {
output = output + "result2 != compared\n";
}
System.out.print(output);
}
Running this method produces this output:
num: 0.3
result1: 3.0
result2: 2.9999999999999996
result1 == compared
result2 != compared
From what I know, the value of "factor" is exactly 10, and the value of "compared" is exactly 3, because these integers can be represented exactly by double. But maybe the value of "num" is a little different from 0.3.
Now I want to know:
-
Is the value of "num" 0.3 (exactly)?
-
If the value of "num" is 0.3, then, why is "result2 == compared" false?
-
If the value of "num" is not 0.3, then, why is its String output "0.3"? From what I know, binary floating-point numbers can always be represented exactly in decimal numbers. So, if the value of "num" is not 0.3, its String output should not be "0.3", but should be something like "0.300000000001".
-
If the value of "num" is not 0.3, then, why is "result1 == compared" true?
-
If the value of "num" is not 0.3, then, what is it?
答案1
得分: 7
- "num" 的值是 0.3 吗(确切地说)?
不是,它是 0.299999999999999988897769753748434595763683319091796875。
- 如果 "num" 的值是 0.3,那么为什么 "result2 == compared" 为假?
前提是错误的;"num" 的值不是 0.3。此外,"result2" 是通过九次加法计算得出的。在每次加法中,都有一个舍入步骤。这些带有舍入的加法的结果是 2.999999999999999555910790149937383830547332763671875。
- 如果 "num" 的值不是 0.3,那么为什么它的字符串输出是 "0.3"?据我所知,二进制浮点数在十进制中始终可以精确表示。因此,如果 "num" 的值不是 0.3,它的字符串输出不应该是 "0.3",而应该是类似于 "0.300000000001" 的东西。
Java 规范指出,对于 double
的默认格式化是只打印足够的小数位数以唯一区分该数字。 具体来说,它将转换为具有将十进制数转换回 double
类型产生原始值的最少位数的十进制数。
将 0.3 转换为 double
会产生最接近的可表示值,即 0.299999999999999988897769753748434595763683319091796875,因此,当使用默认格式打印 0.299999999999999988897769753748434595763683319091796875 时,Java 会产生 "0.3"。
- 如果 "num" 的值不是 0.3,那么为什么 "result1 == compared" 为真?
"num" 是 0.299999999999999988897769753748434595763683319091796875。将它乘以 10 会产生 2.99999999999999988897769753748434595763683319091796875,使用实数运算,但是这个数字在 double
中是不能表示的。浮点数乘法操作会产生最接近的可表示为 double
的值,即 3。
- 如果 "num" 的值不是 0.3,那么它是什么?
它是 0.299999999999999988897769753748434595763683319091796875。
英文:
> 1. Is the value of "num" 0.3 (exactly)?
No, it is 0.299999999999999988897769753748434595763683319091796875.
> 2. If the value of "num" is 0.3, then, why is "result2 == compared" false?
The premise is false; the value of num
is not 0.3. Further, result2
is computed using nine additions. In each addition, there is a rounding step. The result of these additions with rounding is 2.999999999999999555910790149937383830547332763671875.
> 3. If the value of "num" is not 0.3, then, why is its String output "0.3"? From what I know, binary floating-point numbers can always be represented exactly in decimal numbers. So, if the value of "num" is not 0.3, its String output should not be "0.3", but should be something like "0.300000000001".
The Java specifiation says that the default formatting for a double
is to print just enough decimal digits to uniquely distinguish the number. Specifically, it converts to the decimal numeral with the fewest digits having the property that converting the decimal numeral back to the double
type yields the original value.
Converting 0.3 to double
produces the nearest representable value, 0.299999999999999988897769753748434595763683319091796875, so, when printing 0.299999999999999988897769753748434595763683319091796875. with default formatting, Java produces “0.3”.
> 4. If the value of "num" is not 0.3, then, why is "result1 == compared" true?
num
is 0.299999999999999988897769753748434595763683319091796875. Multiplying it by 10 would produce 2.99999999999999988897769753748434595763683319091796875 using real-number arithmetic, but this number is not representable in double
. The floating-point multiplication operation produces the nearest value representable in double
, which is 3.
> 5. If the value of "num" is not 0.3, then, what is it?
It is 0.299999999999999988897769753748434595763683319091796875.
答案2
得分: 1
为了了解这里发生了什么,让我们在十进制中进行一个类似的问题,不涉及二进制浮点数。我们将使用十进制浮点数,精确到三个有效数字。
我们将从值1/3开始,然后将其乘以10,这应该给我们10/3。
但当然,我们不能在十进制中精确表示1/3。在三个有效数字的情况下,我们能够得到的最接近值是0.333。但这已经非常接近了。
如果我们将0.333乘以10,显然得到3.33。这与10/3(应为3.33333…)不完全相同,但仍然非常接近。
但假设我们将0.333加到自己身上10次。这应该得到相同的答案,对吗?
0.333 + 0.333 + 0.333等于0.999。到目前为止都没问题。
0.999 + 0.333应该是1.332。但我们只有三个有效数字的精度。因此,我们必须将其四舍五入为1.33。
1.33 + 0.333应该是1.663。但我们只有三个有效数字的精度。因此,我们必须将其四舍五入为1.66。
1.66 + 0.333应该是1.993。但我们必须将其四舍五入为1.99。
(你开始看到一个模式了吗?情况即将变得更糟。)
1.99 + 0.333应该是2.323,四舍五入为2.32。
2.32 + 0.333应该是2.653,四舍五入为2.65。
2.65 + 0.333应该是2.983,四舍五入为2.98。
最后,2.98 + 0.333应该是3.313,四舍五入为3.31。
所以0.333乘以10是3.33,但0.333 + 0.333 + 0.333 + … + 0.333是3.31。为什么我们得到了两个不同的答案?
因为每次我们添加0.333时,我们都必须将结果四舍五入,而在这种情况下,这总是意味着丢弃0.003。在七个四舍五入的加法步骤之后,这些小的四舍五入误差累积起来,显著影响了最终结果。
当你在二进制中重复添加0.3时,基本上发生了完全相同的事情。0.3在二进制中不能精确表示,所以每次你都不是精确地添加0.3。在九次加法之后,四舍五入误差导致的结果明显不同于当你只是将0.3乘以10。有关更多详细信息,请参阅Eric Postpischil的答案。
英文:
To see what's going on here, let's do a similar problem in decimal, instead — that is, not involving binary floating-point at all. We're going to use decimal floating point, limited to three significant digits of precision.
We're going to start with the value 1/3, and we're going to multiply it by 10, which should give us 10/3.
But of course, we can't represent 1/3 exactly in decimal. The closest we can get, with 3 significant digits, is 0.333. But that's pretty close.
If we take 0.333, and multiply it by 10, we obviously get 3.33. That's not quite the same as 10/3 (which would be 3.33333…), but again, it's pretty close.
But suppose we take 0.333 and add it to itself 10 times. That should give the same answer, right?
0.333 + 0.333 + 0.333 is 0.999. All right so far.
0.999 + 0.333 should be 1.332. But we've only got three significant digits of precision. So we're going to have to round that off to 1.33.
1.33 + 0.333 should be 1.663. But we've only got three significant digits of precision. So we're going to have to round that off to 1.66.
1.66 + 0.333 should be 1.993. But we're going to have to round that off to 1.99.
(Are you beginning to see a pattern? It's about to get a little worse.)
1.99 + 0.333 should be 2.323, which rounds off to 2.32.
2.32 + 0.333 should be 2.653, which rounds off to 2.65.
2.65 + 0.333 should be 2.983, which rounds off to 2.98.
Finally, 2.98 + 0.333 should be 3.313, which rounds off to 3.31.
So 0.333 × 10 was 3.33, but 0.333 + 0.333 + 0.333 + … + 0.333 is 3.31. Why did we get two different answers?
Because just about every time we added 0.333, we had to round off the result, and (in this case) that always meant throwing away 0.003. And after seven addition steps which rounded, those little roundoff errors added up to something that significantly affected the final result.
And more or less exactly the same thing goes on when you repeatedly add 0.3 in binary. 0.3 is not exactly representable in binary, so you weren't adding exactly 0.3 each time. You were adding a value slightly different than 0.3, and after nine additions, the roundoff errors added up to something significantly different than when you just multiplied 0.3 by 10. See Eric Postpischil's answer for more details.
答案3
得分: 0
以下是翻译好的内容:
由于所有十进制数字的范围是无限的,而任何数据类型都有有限的空间,您无法使用双精度数据类型表示所有可能的十进制数字。这就是为什么一些数字会被四舍五入到它们下一个可能的表示方式。
有关实现的更多详细信息,请查阅IEEE 754标准。
英文:
Since the range of all decimal numbers is infinite and any data type has finite space, you cannot represent all possible decimal numbers using a double data type. That's why some numbers will be rounded to their next possible representation.
For more details on the implementation, check the IEEE 754 standard.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论