余数运算符是否容易出现浮点错误?

Is the remainder operator susceptible to floating point errors?

我想为双精度变量 num 创建一个 setter,但我只想在输入是 0.5 的倍数时更新它。

这是我的,但我担心浮点错误。

public void setNum(double num) {
    if (num % 0.5 == 0.0) {
        this.num = num;
    }
}

我假设对于一些实际上是 0.5 的倍数的输入,它可能 return 一些 0.0000003 或 0.49999997,因此不是 0.0。

我该怎么做才能解决这个问题?或者在这种情况下这不是问题?

除非您处理的是非常大的浮点数,否则对于实际上是 0.5 的整倍数的值,您不会失去准确性,因为 0.5 可以用二进制精确表示。但是对于足够接近 0.5 倍数的数字,您可能会发现(例如)10.500000000000000001 已存储为 10.5。

所以如果 num 是 0.5 的倍数,那么 (num % 0.5 == 0.0) 肯定是正确的,但如果 num 是一个接近的数字的稍微不准确的表示,它也可能是正确的到 0.5 的倍数。

Java 的 % 运算符从不引入任何舍入误差,因为结果总是足够小,能够表示精确的余数。

Java 语言规范,Java SE 11 版,15.7.3 为不涉及 NaN、无穷大或零的情况定义了%

In the remaining cases, where neither an infinity, nor a zero, nor NaN is involved, the floating-point remainder r from the division of a dividend n by a divisor d is defined by the mathematical relation r = n - (dq) where q is an integer that is negative only if n/d is negative and positive only if n/d is positive, and whose magnitude is as large as possible without exceeding the magnitude of the true mathematical quotient of n and d.

因此r的量级不大于n的量级(因为我们减去一些dq 来自 n 的量级小于 n 且为零或具有与 n 相同的符号)并且小于 d 的大小(否则 q 可能幅度大一倍)。这意味着 r 至少与 nq 一样好——它的指数至少和 n 一样小作为 n 的指数和 q 的指数。这意味着 n - (dq) 的二进制表示中没有有效位在下面r的最低位的位置值。因此,没有重要位超出 r 必须四舍五入的点。所以四舍五入没有丢失任何东西。所以 r 是一个精确的结果。