取模运算不起作用。

6

我知道这可能是一个非常愚蠢的问题,但我就是不明白为什么它不能正常工作。这是:

 System.out.println(5 % .10);

并且它返回:

0.09999999999999973

我真的不知道。我正在学习Java,并且我对C#也很熟练,所以我也尝试使用C#来解决问题。但C#似乎返回了相同的结果。


浮点数之间的模数是什么? - Oscar Korz
你认为它应该返回什么? - Shahzeb
@Shahzeb 他可能期望得到0,因为0.1可以被5整除。 - Oscar Korz
1
模数并没有被破解。但可以肯定的是,您可能对模数和浮点表示法存在误解。 - John Percival Hackworth
5个回答

8
这是由于浮点数精度问题导致的。0.10在二进制中无法被准确表示。因此,结果并不完全是0.10,因此不能被模除为0
这对于0.25有效,因为0.25可以在二进制中准确表示。
编辑:
一般来说,任何能够用分母为2的幂次表达为分数的数字都可以在IEEE浮点数中准确表示。(前提是它不会溢出/下溢)

那么我该如何获得0.10的模数呢?只需将5乘以100,将0.10乘以100即可。 - hetelek
你可以将5乘以10并取模1。然后将结果除以10。 - Mysticial

8

正如其他人所解释的那样,这是由于浮点精度不准确导致的。

在涉及小数的精确算术中,您应该使用BigDecimal,在这种情况下使用余数方法

BigDecimal number = new BigDecimal(5);
BigDecimal divisor = new BigDecimal("0.1");
BigDecimal result = number.remainder(divisor);
System.out.println(result); // 0

2

您正在进行浮点运算。0.1在浮点数中没有精确的表示。


不太确定你的意思,但它似乎可以与其他浮点数一起工作。如果我执行 "System.out.println(5 % .25);" 它将返回 0。谢谢 :) - hetelek
1
他的意思就是他所说的。0.1无法在IEEE-754浮点数中表示。这是一个经典的计算机科学问题。 - Oscar Korz
0.25 可以被表示为 2 的幂次方(2 ^ -2)。 - Oscar Korz
2
@sh042067:这是因为0.25可以在数字计算机上精确表示,它等同于1/4。请搜索有关浮点数的数字表示的谷歌资料。这不是Java问题,而是数字计算机问题。 - Hovercraft Full Of Eels

1

0.1 的二进制表示为

System.out.println(new BigDecimal(0.1));

打印

0.1000000000000000055511151231257827021181583404541015625

当你打印0.1时,会有一点四舍五入的情况出现,这掩盖了这个错误。

在进行计算时,必须使用BigDecimal或将结果四舍五入或转换计算以最小化误差。

5 % 0.1
(5 / 0.1 % 1) * 0.1
50 % 1 / 10

关于 double 类型,你可以这样做

double d = 5;
double mod0_1 = d * 10 % 1 / 10;
double rounded = Math.round(mod0_1 * 1e12)/1e12; // round to 12 places.

注意:结果仍可能存在轻微错误,但足够小,以至于在打印时您看不到它。

1

Java(像大多数编程语言一样,除了COBOL)在二进制系统中进行计算。我认为0.10具有如此不幸的二进制表示形式,以至于您的计算结果看起来像这样。我认为最好避免计算双精度或浮点数的模数,并坚持使用整数或长整型以获得更好的结果。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接