Быстро квадратный двойной
Я ищу самый быстрый способ свернуть двойной (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()
медленный, поскольку он должен иметь дело с общим случаем или поднимать число до любой заданной мощности.
Что касается того, почему он медленнее с отрицательными числами, это потому, что он должен проверить, является ли сила положительной или отрицательной, чтобы дать знак, так что это еще одна операция.