【问题标题】:Modulus with doubles in JavaJava中的双精度模数
【发布时间】:2023-03-04 19:01:01
【问题描述】:

在使用双精度时,如何处理 Java 中模运算符的怪异行为?

例如,您希望3.9 - (3.9 % 0.1) 的结果是3.9(实际上,Google 表示我不会发疯),但是当我在 Java 中运行它时,我得到了3.8000000000000003

我知道这是 Java 存储和处理双倍的结果,但是有没有办法解决它?

【问题讨论】:

  • 你想做什么?我有一种感觉,你真的根本不需要%

标签: java double modulus


【解决方案1】:

如果您知道要处理的小数位数,您可以先尝试转换为整数。这只是浮点不准确的典型案例。而不是做3.9 % 0.1 你正在做类似3.899 % 0.0999

【讨论】:

  • Google 会声称 3.9 % 0.1 = -4.4408921 × 10-16 也不太适合模数。
【解决方案2】:

您可以使用 java.math.BigDecimal 及其方法 divideAndRemainder()。

【讨论】:

    【解决方案3】:

    如果您需要精确的结果,请使用精确类型:

        double val = 3.9 - (3.9 % 0.1);
        System.out.println(val); // 3.8000000000000003
    
        BigDecimal x = new BigDecimal( "3.9" );
        BigDecimal bdVal = x.subtract( x.remainder( new BigDecimal( "0.1" ) ) );
        System.out.println(bdVal); // 3.9
    

    为什么是 3.8000...003?因为Java使用FPU来计算结果。 3.9 不可能以 IEEE 双精度表示法精确存储,因此它存储 3.89999... 代替。而 3.8999%0.01 给出 0.09999...因此结果比 3.8 大一点。

    【讨论】:

    • +1 我讨厌在电气工程课程中手动使用 IEEE 浮点数,但这是值得的。
    • 不错的答案。那么为什么当 FPU 认为 (3.9D % 0.1D) = 0.0999999999999997 时由于舍入错误而能够找出 ((3.9D / 0.1D) % 1D) = 0.0 (基本上相同的问题略有重新组织)?
    • @DaveJohnson:这不好说。当你这样做时,舍入误差可能会累积起来(即模块创建的分数太小)。
    • @bcsb1001:正如我在回答中解释的那样:它也不起作用。一些舍入错误从double 泄漏到BigDecimal。另外:您经常从输入端(文本文件、UI 输入字段)获取字符串并在创建 BigDecimal 之前将它们转换为 double 是浪费时间加上(小)风险。
    • @bcsb1001:在 OpenJDK 8u91 中,BigDecimal#valueOf(double) 实现为 new BigDecimal(Double.toString(x))。原因显然是 Java 语言规范要求浮点到字符串的转换具有比 IEEE 对一般浮点算术的要求更高的精度。
    【解决方案4】:

    来自The Java Language Specification

    浮点数的结果 由计算的余数运算 % 运算符不一样 由余数运算产生 由 IEEE 754 定义。IEEE 754 余数运算计算 舍入除法的余数, 不是截断除法,所以它的 行为不类似于 通常的整数余数运算符。 相反,Java 编程语言 定义浮点运算的 % 以类似的方式行事 整数余数的 操作员;这可以与 C 库函数 fmod。 IEEE 754 余数运算可能是 由库例程计算 Math.IEEE 余数。

    换句话说,这是因为 Java 将计算余数的除法结果四舍五入,而 IEEE754 指定截断除法结果。这个特殊情况似乎非常清楚地暴露了这种差异。

    您可以使用 Math.IEEEremainder 得到您期望的答案:

    System.out.println(3.9 - (3.9 % 0.1));
    System.out.println(3.9 - Math.IEEEremainder(3.9, 0.1));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多