此问题已在此处有答案:
Is floating point math broken?(32个回答)
9小时前关闭。
我正试图更深入地理解浮点数。我知道二进制(基数为2)浮点数不能精确地表示某些十进制数。但是我对Java中double的这种奇怪行为感到困惑(在Java中,double是一种二进制浮点数数据类型)。
我写了这段Java代码:
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);
}
运行此方法会产生以下输出:
num: 0.3
result1: 3.0
result2: 2.9999999999999996
result1 == compared
result2 != compared
据我所知,“factor”的值正好是10,“compared”的值正好是3,因为这些整数可以用double精确表示。但也许“num”的值与0.3有一点不同。
现在我想知道:
1.“num”的值是否为0.3(精确)?
1.如果“num”的值是0.3,那么,为什么“result2 == compared”是false?
1.如果“num”的值不是0.3,那么,为什么它的String输出是“0.3”?据我所知,二进制浮点数总是可以精确地用十进制数表示。因此,如果“num”的值不是0.3,则其String输出不应是“0.3”,而应类似于“0.30000000001”。
1.如果“num”的值不是0.3,那么,为什么“result1 == compared”为true?
1.如果“num”的值不是0.3,那么,它是什么?
3条答案
按热度按时间4zcjmb1e1#
1.“num”的值是否为0.3(精确)?
不,它是0.2999999999999998897769753748434595763683319091796875。
1.如果“num”的值是0.3,那么,为什么“result 2 == compared”是false?
前提是假的;
num
的值不是0.3。此外,result2
使用九次加法来计算。在每次加法中,存在舍入步骤。四舍五入后的结果是2.99999999999999955910790149937383830547332763671875。1.如果“num”的值不是0.3,那么,为什么它的String输出是“0.3”?据我所知,二进制浮点数总是可以精确地用十进制数表示。因此,如果“num”的值不是0.3,则其String输出不应是“0.3”,而应类似于“0.30000000001”。
The Java specifiation says that the default formatting for a
double
is to print just enough decimal digits to uniquely distinguish the number.具体地说,它转换为具有最少位数的十进制数,该十进制数具有将十进制数转换回double
类型将产生原始值的属性。将0.3转换为
double
会产生最接近的可表示值0.299999999999999998897769753748434595763683319091796875,因此,当打印0.2999999999999999999988976975374843459576368319091796875时。在默认格式下,Java生成“0.3”。1.如果“num”的值不是0.3,那么,为什么“result 1 == compared”为true?
num
是0.2999999999999998897769753748434595763683319091796875。用实数算术将其乘以10将产生2.99999999999999998897769753748434595763683319091796875,但这个数字在double
中无法表示。浮点乘法运算产生可在double
中表示的最近值,即3。1.如果“num”的值不是0.3,那么,它是什么?
它是0.2999999999999998897769753748434595763683319091796875。
2fjabf4q2#
为了了解这里发生了什么,让我们用十进制来做一个类似的问题,也就是说,根本不涉及二进制浮点数。我们将使用 * decimal * 浮点,精度限制为 * 三个有效数字 *。我们从1/3开始,然后乘以10,得到10/3。
当然,我们不能用十进制来表示1/3。我们可以得到的最接近的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.
(Are你开始看出规律了吗它会变得更糟一点)。
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 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 + 0.333 ++0.333等于3.31。为什么我们得到了两个不同的答案?
因为几乎每次我们添加0.333时,我们都必须对结果进行四舍五入,并且(在这种情况下)这总是意味着丢弃0.003。经过七次四舍五入的加法步骤,这些小的四舍五入误差加起来会对最终结果产生重大影响。
当你在二进制中反复加0.3时,或多或少会发生同样的事情。0.3在二进制中不能精确表示,所以你不是每次都加0.3。您添加了一个与0.3略有不同的值,并且在九次添加之后,舍入误差的总和与您将0.3乘以10时的结果明显不同。请参阅Eric Postpischil的回答以了解更多细节。
lhcgjxsq3#
由于所有十进制数的范围都是无限的,并且任何数据类型的空间都是有限的,因此无法使用double数据类型表示所有可能的十进制数。这就是为什么一些数字将四舍五入到它们的下一个可能的表示。
有关实现的更多详细信息,请查看the IEEE 754 standard。