我猜这个问题以前已经被问过了,但似乎找不到匹配的问题......
我正在使用Scala,但我很确定这只是一个Java问题... 输入值是双精度浮点数。
println(28.0 / 5.6)
println(28.0 % 5.6)
这几行的结果是:
5.0
1.7763568394002505E-15
这意味着Java在执行除法时是正确的,但由于某种原因获取了错误的模数,因为对于任何解决为整数的除法问题,模数应为0...
有没有解决方法?
谢谢!
我猜这个问题以前已经被问过了,但似乎找不到匹配的问题......
我正在使用Scala,但我很确定这只是一个Java问题... 输入值是双精度浮点数。
println(28.0 / 5.6)
println(28.0 % 5.6)
5.0
1.7763568394002505E-15
这意味着Java在执行除法时是正确的,但由于某种原因获取了错误的模数,因为对于任何解决为整数的除法问题,模数应为0...
有没有解决方法?
谢谢!
5.0的意思是,Java理解的精确结果比任何其他double更接近5.0。这并不意味着操作的精确结果是完全等于5.0。
当您请求模数时,可以对细节进行更精细的调整,因为结果不被固定为具有“5”部分。
这不是一个很好的解释,但是想象一下你有一个带有4位精度的小数浮点类型。1000/99.99和1000%99.99的结果是什么?
实际结果以10.001001开头 - 因此您必须将其四舍五入为10.00。然而,余数为0.10,您可以表示。所以再次,看起来除法给您整数,但它并不完全是整数。
请记住,您的字面值为5.6,实际上是5.5999999999999996447286321199499070644378662109375。现在显然28.0(可以)被准确表示为该数字的商,但并非完全等于5。
编辑:如果使用BigDecimal
执行具有十进制浮点算术,则该值确实完全等于5.6,并且没有问题:
import java.math.BigDecimal;
public class Test {
public static void main(String[] args) {
BigDecimal x = new BigDecimal("28.0");
BigDecimal y = new BigDecimal("5.6");
BigDecimal div = x.divide(y);
BigDecimal rem = x.remainder(y);
System.out.println(div); // Prints 5
System.out.println(rem); // Prints 0.0
}
}
double
能够表示这一点,因为它不必表示5和1之间的所有数字。 - StriplingWarriorfloat
或double
作为模运算符的参数来解决这个问题 :) - QuantumMechanic首先,十进制数5.6无法在二进制浮点数中被精确表示。它被舍入为精确的二进制分数3152519739159347/249。
28.0 / 5.6 = 5.0的原因是因为5.0是最接近真实结果的double
数字,而真实结果是5.0000000000000003172065784643....
至于28.0%5.6,真实结果恰好为1/249,约为1.776×10−15,因此计算是正确舍入的。
为什么需要解决方法?对于大多数应用程序来说,保留非常微小的错误结果就足够了。您是否关心显示“漂亮”的结果?
如果您需要绝对精确的算术运算,则需要使用BigFraction的某些实现。
浮点数注意事项的主题已被涵盖在各种文章中:
(按读者友好性递减的顺序。)
这是我使用模运算符检查双精度值是否可被另一个数整除的解决方案:
public class DoubleOperation
{
public static final double EPSILON = 0.000001d;
public static boolean equals(double val1, double val2)
{
return (Math.abs(val1 - val2) < EPSILON);
}
public static boolean divisible(double dividend, double divisor)
{
double divisionRemainder = dividend % divisor;
return (equals(divisionRemainder, 0.0d) || equals(divisionRemainder, divisor));
}
}
double
根本不是十进制数学系统,而是二进制数学系统。一旦你知道了这一点,其他的就很容易了。如果你使用正确的类型,在Java中也不需要考虑这一点;但我认为合理地期望软件工程师了解所使用语言中类型的基础知识(而不是细节)。重要的是要明白,double
不是十进制浮点类型(例如C#中的decimal
)。对我来说,这就像知道String
由字符而不是字节组成以及为什么这很重要。 - Jon Skeet