浮子型没用?

Float-type useless?

提问人:owns good 提问时间:10/26/2022 更新时间:10/26/2022 访问量:101

问:

在我的 Java 书中,它告诉我在将 2 个不同的 float(type) 数字存储在变量中时不要直接比较它们。因为它在变量中给出了一个近似的数字。相反,它建议检查差值的绝对值,看看它是否等于 0。如果是这样,它们是相同的。这有什么帮助?如果我将 5 存储在变量 a 中,将 5 存储在变量 b 中,它们怎么可能不相同呢?如果我比较绝对值,它有什么帮助?

double a=5,b=5;

if (Math.abs(a-b)==0)
//run code

if (a==b)
//run code 

我完全不明白为什么上述方法会更准确?因为如果“a”不等于“b”,那么我是否使用 Math.abs 也没关系。

感谢您的回复,并感谢您抽出宝贵时间接受采访。

我尝试了这两种方法。

Java 浮点

评论

3赞 Some programmer dude 10/26/2022
浮点变量中的值是存储在变量中的值,获取它不会使值更好或更差。因此,“它在变量中给出了一个近似的数字”是不正确的。事实是,对浮点值(加法、乘法、除法等)进行运算可能会(但并非总是取决于值)导致较小的舍入误差。对值进行的操作越多,误差就越大。因此,在对变量进行一些运算后,直接相等性比较可能不会产生预期的结果。
1赞 Some programmer dude 10/26/2022
解决方案通常是使用 epsilon 进行比较。与您的方法类似:absif (Math.abs(a - b) < 0.00001)
1赞 Some programmer dude 10/26/2022
另请参阅浮点数学是否损坏?
0赞 owns good 10/26/2022
哦!是的,这要聪明得多。喜欢设置为 <0.0001 做得很好。这意味着即使在此过程中出现小错误,大多数时候这仍然不是那么大的差异。我明白了,所以不要将 ==0 设置为 <0.0001。我为什么没有想到这一点。谢谢!
0赞 chux - Reinstate Monica 11/2/2022
这两种方法都很弱。

答:

2赞 Ali Hamed 10/26/2022 #1

使用 == 运算符进行比较不准确是由双精度值在计算机内存中的存储方式引起的。我们需要记住,有限的内存空间(通常是 64 位)必须容纳无限数量的值。因此,我们无法在计算机中精确表示大多数双精度值。它们必须四舍五入才能保存。

由于舍入不准确,可能会出现有趣的错误:

double d1 = 0;
for (int i = 1; i <= 8; i++) {
d1 += 0.1;
}

double d2 = 0.1 * 8;

System.out.println(d1);
System.out.println(d2);

两个变量 d1 和 d2 都应等于 0.8。但是,当我们运行上面的代码时,我们将看到以下结果:

0.7999999999999999
0.8

在这种情况下,使用 == 运算符比较这两个值将产生错误的结果。因此,我们必须使用更复杂的比较算法。

如果我们想对舍入机制有最好的精度和控制,我们可以使用 java.math.BigDecimal 类。

在纯 Java 中比较双精度值的推荐算法是一种阈值比较方法。在这种情况下,我们需要检查两个数字之间的差值是否在指定的容差范围内,通常称为epsilon:

double epsilon = 0.000001d;

assertThat(Math.abs(d1 - d2) < epsilon).isTrue();

epsilon 的值越小,比较精度越高。但是,如果我们指定的公差值太小,我们将得到与简单 == 比较相同的错误结果

评论

0赞 rzwitserloot 10/26/2022
欢迎来到 SO,@AliHamed;一个很好的答案。一些亮点只是为了让提问者清楚:相反,它建议检查差值的绝对值,看看它是否等于 0 - 我怀疑它做到了这一点;你检查差值的绝对值,看看它是否是一个非常小的数字,如这个答案所示。不是 0。因为它在变量中给出了一个近似的数字一些数字。“5”可以完美存储。0.1 不能。每个操作都舍入到最接近的可表示对象。做很多操作,就像这个答案一样 - 四舍五入会让你。
0赞 chux - Reinstate Monica 11/2/2022
Math.abs(d1 - d2) < epsilon采取浮出的有用性。当 和 是连续的 ,并且像 1e20 这样大,总是假的。当 和 不接近连续 ,并且像 1e-20 一样小,总是正确的。 应根据所涉及的值的大小进行缩放。doubled1d2doubleMath.abs(d1 - d2) < epsilond1d2doubleMath.abs(d1 - d2) < epsilonepsilon
1赞 Кирилл Герасименко 10/26/2022 #2

问题是,你在 java 书中读到的那句话只是防止你将来出现一些错误,这些错误很难调试。计算机将小数/浮点数存储为二进制,因此并非所有我们可以用十进制数表示为有理数的东西都可以用二进制表示为有理数,所以总是有类似 0.7 = 0.6999999999998511 的东西。在你使用的那些比较中,你可能不会看到差异,但在实际项目中,你可能会使用更多的变量,从中加减,这种差异可能会出现在非常令人惊讶的地方。

关于浮点数有一些经典的问题。您也可以在任何语言中看到它 为什么此代码打印“7”的结果?