【问题标题】:Is the remainder operator susceptible to floating point errors?余数运算符是否容易受到浮点错误的影响?
【发布时间】:2021-11-18 15:39:03
【问题描述】:

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

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

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

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

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

【问题讨论】:

  • 不是取模算子,是余数算子,任何浮点算子都服从浮点数的规则。
  • @user207421 感谢您指出这一点!我已经编辑了问题标题

标签: java floating-accuracy


【解决方案1】:

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

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

【讨论】:

  • 没有“数字的稍微不准确的表示”这样的事情。根据 IEEE-754 标准,浮点格式的每个数据准确地表示一个数字或者是一个 NaN。 (在这方面,无穷大被认为是一个数字。)错误发生在操作中,而不是表示中。当执行任何操作时,它产生的结果等于四舍五入到最接近的可表示值的实数算术结果。结果不代表实数算术结果;它只是一个不同的数字......
  • ...了解这种区别对于分析、证明和调试浮点软件至关重要。
  • 从技术上讲,这是double 精度有限的问题,不是 % 运算符的问题。
【解决方案2】:

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

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-17
    • 1970-01-01
    • 1970-01-01
    • 2018-07-09
    • 2012-06-11
    相关资源
    最近更新 更多