【问题标题】:Quickly square a double快速平方双倍
【发布时间】:2026-02-07 15:15:01
【问题描述】:

我正在寻找求双精度平方的最快方法 (double d)。到目前为止,我想出了两种方法:

1. d*d
2. Math.pow(d, 2)

为了测试性能,我设置了三个测试用例,在每个测试用例中,我使用相同的种子为这三个用例生成随机数,并在循环中计算平方数 100 000 000 次。

在第一个测试用例中,数字是使用random.nextDouble() 生成的,在第二个测试用例中使用random.nextDouble()*Double.MAX_VALUE,在第三个测试用例中使用random.nextDouble()*Double.MIN_VALUE

几次运行的结果(近似结果,总有一些变化,使用 java 1.8 运行,在 Mac OSX Mavericks 上为 java 1.6 编译)

Approach | Case 1 | Case 2 | Case 3
---------•--------•--------•-------
    1    | ~2.16s | ~2.16s | ~2.16s
    2    | ~9s    | ~30s   | ~60s

结论似乎是方法 1 更快,但Math.pow 的行为似乎有点奇怪。

所以我有两个问题:

  1. 为什么Math.pow 这么慢,为什么它不能很好地处理> 1 甚至更糟糕的< -1 数字?

  2. 有没有办法比我建议的方法 1 提高性能?我正在考虑类似的事情:

    long l = Double.doubleToRawLongBits(d);
    long sign = (l & (1 << 63));
    Double.longBitsToDouble((l<<1)&sign);
    

但这是 a) 错误的,b) 与方法 1 的速度差不多。

【问题讨论】:

  • 我没有参考,但我想我记得Math.pow() 只有在使用更大的指数时才有用。如果更简单的方法似乎更快地进行平方,为什么不坚持下去呢?
  • random.nextDouble() 在你的长循环中吗?如果是这样,您主要衡量的是那个,而不是d*d
  • @MikeDunlavey 当然,这些数字在绝对测量中并没有多大意义,但由于在所有循环中生成的数字都是相同的,您可以进行比较

标签: java performance math multiplication


【解决方案1】:

乘以自我平方是最快的。因为该方法可以直接转换为简单的非分支字节码(因此,间接地,机器码)。

Math.pow() 是一个非常复杂的函数,它为边缘情况提供了各种保证。它需要被调用而不是被内联。

【讨论】:

    【解决方案2】:

    Math.pow() 很慢,因为它必须处理一般情况或将数字提高到任何给定的幂。
    至于为什么用负数比较慢,是因为它必须测试幂是正数还是负数才能给出符号,所以要多做一次操作。

    【讨论】:

    • 我不认为一个额外的操作会导致这么大的差异
    • 鉴于javadoc,它会在测试是否为负之前测试许多情况,例如NaN,数字和幂的无穷大等。来源:link
    • + 写Math.pow 的人必须假设用户有一定的智慧。大多数情况下,它用于将正数提高到任意幂。通过简单的乘法很容易得到小的积分幂。
    【解决方案3】:

    求平方的最快方法是乘以它自己。

    为什么Math.pow 这么慢?

    确实不是,但它执行的是exponentiation,而不是简单的乘法。

    为什么它对 > 1 的处理不好,甚至对

    首先,因为它会进行数学运算。从Javadoc 开始,它还包含针对许多极端情况的测试。最后,我不会过分依赖您的微基准测试。

    【讨论】:

    • Math.pow 也是通过 StrictMath 原生实现的