如何在浮点数/双精度数中使用模运算?

56
我正在为一项校内项目创建一个RPN计算器,但是在求模运算符方面遇到了问题。由于我们使用的是double数据类型,所以模运算符无法用于浮点数。例如,0.5 % 0.3 应该返回0.2,但我得到了除零异常的结果。
指令中提到要使用fmod()。我已经在包括javadoc在内的所有地方寻找了fmod(),但是我找不到它。我开始觉得这可能是我需要创建的方法?
编辑:嗯,奇怪。我刚才又输入了那些数字,似乎也能正常工作...但以防万一,我需要注意在使用浮点类型时使用Java中的mod运算符吗?我知道在C++中不能做这样的事情(我想)。
4个回答

78

你第一次运行时可能打错了字。

评估 0.5 % 0.3 返回预期的 '0.2' (一个双精度数)。

Mindprod对Java中模数的工作原理有一个很好的概述


3
但是1.0 % 0.1返回了0.09999999999999995,这是因为舍入误差导致的结果。有什么办法可以纠正这个问题(可能需要使用epsilon值)? - Groostav
4
在浮点数计算中,舍入误差总是会发生。要么接受给定的值,要么将其四舍五入到最接近的必要值。 - Anarchofascist
String.format("%.8f",1.0%0.1) returns 0.10000000 - Anarchofascist
1
@Groostav 这不是一个四舍五入误差。0.1 无法表示为双精度浮点数,因此结果是最接近可表示的双精度浮点数。 - augurar
1
这比结果仅仅被四舍五入更糟糕。你说的是 1.0 % 0.1,但实际上你做的是 1.0 % 0.10000000000000000555111512...,所以如果你增加那个 1.0,答案将会越来越远离 0.1。 - Mark Jeronimus
显示剩余2条评论

49

与C语言不同,Java允许在整数和浮点数中使用%,并且(与C89和C++不同)对所有输入都有良好的定义:

来自JLS §15.17.3

浮点余数运算的结果由IEEE算术规则确定:

  • 如果任一操作数是NaN,则结果为NaN。
  • 如果结果不是NaN,则结果的符号等于被除数的符号。
  • 如果被除数是无穷大,或者除数是零,或者两者都是,则结果为NaN。
  • 如果被除数是有限的且除数是无穷大,则结果等于被除数。
  • 如果被除数是零且除数是有限的,则结果等于被除数。
  • 在其余情况下,既不涉及无穷大、零、也不涉及NaN,从被除数n除以除数d的商中得到的浮点余数r由数学关系r=n-(d·q)定义,其中q是一个整数,仅在n/d为负时为负,在n/d为正时为正,并且其大小尽可能大而不超过n和d的真实数学商的大小。
所以对于你的例子,0.5/0.3 = 1.6 ...。q与0.5(被除数)具有相同的符号(正),大小为1(最大幅度的整数不超过1.6的幅度),并且r = 0.5 -(0.3 * 1)= 0.2。

11
最后一个要点文字太长了。通过一个例子来说明可能更清晰: 0.5 % 0.3 = 0.2(-0.5) % 0.3 = -0.2 - CoffeDeveloper

7

我以为在Java中通常的模运算符可以解决这个问题,但编写代码并不难。只需将分子除以分母,并取结果的整数部分。将其乘以分母,再从分子中减去结果。

x = n/d
xint = Integer portion of x
result = n - d*xint

1
在逻辑上是有意义的,但在实际实现中不适用。 - Valen
1
@Valen:更具体地说,它进行了四舍五入,而大多数真实的余数运算(包括Java的“%”和“IEEEremainder”)都是精确的。 - Davis Herring

4

fmod是处理浮点数模的标准C函数;我想您的源代码是在说Java处理浮点数模与C的fmod函数相同。在Java中,您可以像整数一样使用%运算符处理双精度浮点数:

int x = 5 % 3; // x = 2
double y = .5 % .3; // y = .2

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