余数运算符是否容易受到浮点误差的影响?

Is the remainder operator susceptible to floating point errors?

提问人:rhkoulen 提问时间:9/26/2021 最后编辑:rhkoulen 更新时间:9/29/2021 访问量:130

问:

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

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

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

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

我能做些什么来解决这个问题?或者在这种情况下这不是问题吗?

java 浮点精度

评论

0赞 user207421 9/26/2021
它不是模运算符,而是余数运算符,任何浮点运算符都受浮点规则的约束。
0赞 rhkoulen 9/29/2021
@user207421 感谢您指出这一点!我已经编辑了问题标题

答:

4赞 khelwood 9/26/2021 #1

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

因此,如果是 0.5 的倍数,则肯定是正确的,但如果是接近 0.5 倍数的数字的略微不准确的表示,也可能是正确的。(num % 0.5 == 0.0)numnum

评论

3赞 Eric Postpischil 9/26/2021
不存在“数字的略微不准确的表示”之类的东西。根据 IEEE-754 标准,浮点格式的每个基准面正好代表一个数字或一个 NaN。(在这方面,无穷大被认为是一个数字。错误发生的地方是操作,而不是表示。执行任何操作时,它都会生成一个结果,该结果等于四舍五入到最接近的可表示值的实数算术结果。结果不代表实数算术结果;这只是一个不同的数字......
0赞 Eric Postpischil 9/26/2021
...了解这种区别对于分析、证明和调试浮点软件至关重要。
0赞 dan04 10/14/2021
从技术上讲,这是 精度有限的问题,而不是操作员的问题。double%
0赞 Eric Postpischil 9/26/2021 #2

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

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

在其余情况下,既不涉及无穷大,也不涉及零,也不涉及 NaN,则被除数 n 除以除数 d 的浮点余数 r 由数学关系 r = n - (d ⋅ q) 定义,其中 q 是一个整,仅当 n/d 为负时为负,仅n/d 为正时为正是正的,其大小尽可能大,不超过 nd 的真实数学商的大小。

因此,r 的大小不大于 n 的大小(因为我们从 n 中减去一些 d ⋅ q,它的大小小于 n,并且为零或与 n 具有相同的符号),并且小于 d 的大小(因为否则 q 的大小可能大 1)。这意味着 r 至少和 n 和 q 一样精细——它的指数至少和 n 的指数和 q 的指数一样小这意味着 n - (dq) 的二进制表示中没有有效位低于 r 最低位的位置值。因此,没有有效位超过必须对 r 进行舍入的点。因此,在四舍五入中没有丢失任何东西。所以 r 是一个精确的结果。