Быстро квадратный двойной

Я ищу самый быстрый способ свернуть двойной (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, скомпилированные для java 1.6 на Mac OSX Mavericks)

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);

Но это а) неправильно, и б) примерно с той же скоростью, что и подход 1.

Ответ 1

Самый быстрый способ скомпоновать число - это умножить его на себя.

Почему Math.pow так медленно?

Это действительно не так, но он выполняет exponentiation вместо простого умножения.

и почему он плохо справляется s > 1 и еще хуже с < -1 номеров

Во-первых, потому что он выполняет математику. Из Javadoc он также содержит тесты для многих угловых случаев. Наконец, я не стал бы слишком полагаться на ваш микро-тест.

Ответ 2

Квадрагирование путем мультипликации с self является самым быстрым. Поскольку этот подход может быть непосредственно переведен на простой, не ветвящийся байт-код (и, следовательно, косвенно, машинный код).

Math.pow() - довольно сложная функция, которая поставляется с различными гарантиями для случаев краев. И его нужно вызывать вместо того, чтобы быть встроенным.

Ответ 3

Math.pow() медленный, поскольку он должен иметь дело с общим случаем или поднимать число до любой заданной мощности.
Что касается того, почему он медленнее с отрицательными числами, это потому, что он должен проверить, является ли сила положительной или отрицательной, чтобы дать знак, так что это еще одна операция.