通过位操作更快地实现Math.abs()

Tim*_*ers 3 java bit-manipulation absolute-value

Math.abs(x)(由Oracle实施)的常规实现由

public static double abs(double a) {
  return (a <= 0.0D) ? 0.0D - a : a;
}
Run Code Online (Sandbox Code Playgroud)

仅将数字符号的一位编码设置为零(或一位)是否更快?我想只有一个位编码数字的符号,并且它总是相同的位,但是我可能是错的。

还是我们的计算机通常不适合使用原子指令对单个位进行操作?

如果可以更快地实施,您能给出吗?

编辑:

已经向我指出Java代码是平台无关的,因此它不能依赖于单机的原子指令是什么。但是,为了优化代码,JVM热点优化器确实考虑了计算机的具体情况,并且可能会应用正在考虑的最优化方法。

但是,通过一个简单的测试,我发现至少在我的机器上,该Math.abs功能似乎并未针对单个原子指令进行优化。我的代码如下:

    long before = System.currentTimeMillis();
    int o = 0;
    for (double i = 0; i<1000000000; i++)
        if ((i-500)*(i-500)>((i-100)*2)*((i-100)*2)) // 4680 ms
            o++;
    System.out.println(o);
    System.out.println("using multiplication: "+(System.currentTimeMillis()-before));
    before = System.currentTimeMillis();
    o = 0;
    for (double i = 0; i<1000000000; i++)
        if (Math.abs(i-500)>(Math.abs(i-100)*2)) // 4778 ms
            o++;
    System.out.println(o);
    System.out.println("using Math.abs: "+(System.currentTimeMillis()-before));
Run Code Online (Sandbox Code Playgroud)

这给了我以下输出:

234
using multiplication: 4985
234
using Math.abs: 5587
Run Code Online (Sandbox Code Playgroud)

假设乘法是通过原子指令执行的,那么至少在我的机器上,JVM热点优化器似乎并未将Math.abs功能优化为单个指令操作。

Hol*_*ger 5

我的第一个想法是,这是由于NaN(非数字)值所致,即,如果输入为NaN原值,则应返回该值而无需进行任何更改。但是,这似乎不是必需的,因为harold的测试表明JVM的内部优化不能保留NaN的符号(除非您使用StrictMath)。

Math.abs 的文档说:

换句话说,结果与表达式的值相同: Double.longBitsToDouble((Double.doubleToLongBits(a)<<1)>>>1)

因此,此类的开发人员知道位操作的选项,但他们决定反对。

很可能是因为优化此Java代码没有意义。在大多数环境中,热点优化器一旦在热点中遇到,便会将其调用替换为适当的FPU指令。这发生在许多java.lang.Math方法以及Integer.rotateLeft类似方法中。它们可能具有纯Java实现,但是如果CPU有针对它的指令,它将由JVM使用。