3

(由 Oracle 实现)的正常实现Math.abs(x)由下式给出

public static double abs(double a) {
  return (a <= 0.0D) ? 0.0D - a : a;
}

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

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

如果更快的实现是可能的,你能给它吗?

编辑:

有人向我指出,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));

这给了我以下输出:

234
using multiplication: 4985
234
using Math.abs: 5587

假设乘法是由原子指令执行的,似乎至少在我的机器上,JVM 热点优化器没有将Math.abs函数优化为单指令操作。

4

2 回答 2

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 使用。

于 2013-10-29T11:38:14.067 回答
1

我不是 java 专家,但我认为问题在于这个定义可以用语言表达。浮点数上的位操作是特定于机器格式的,因此不可移植,因此在 Java 中是不允许的。我不确定是否有任何 jit 编译器会进行优化。

于 2013-10-29T10:55:33.047 回答