Число.sign () в javascript
интересно, есть ли нетривиальные способы найти знак числа (функция signum)?
Может быть короче / быстрее / более элегантные решения, чем очевидный
var sign = number > 0 ? 1 : number < 0 ? -1 : 0;
короткая выдержка
использовать это, и вы будете в безопасности и быстро
function sign(x) {
return typeof x === 'number' ? x ? x < 0 ? -1 : 1 : x === x ? 0 : NaN : NaN;
}
результаты
на данный момент у нас есть следующие решения:
1. очевидное и быстро!--17-->
function sign(x) { return x > 0 ? 1 : x < 0 ? -1 : 0; }
1.1. модификации kbec - один тип бросил меньше, больше performant, более короткий [быстрый]
function sign(x) { return x ? x < 0 ? -1 : 1 : 0; }
внимание: sign("0") -> 1
2. элегантный, короткий, не так быстро [медленный]
function sign(x) { return x && x / Math.abs(x); }
внимание: sign(+-Infinity) -> NaN
, sign("0") -> NaN
по состоянию на Infinity
является юридическим номером в JS это решение не кажется полностью правильным.
3. искусство... но очень медленно!--30-->[медленный]
function sign(x) { return (x > 0) - (x < 0); }
4. использование bit-shift
быстро, но!--12-->
function sign(x) { return (x >> 31) + (x > 0 ? 1 : 0); }
5. Type-safe [megafast]
! кажется, что браузеры (особенно V8 chrome) делают некоторые волшебные оптимизации, и это решение превращается он гораздо более эффективен, чем другие, даже чем (1.1), несмотря на то, что он содержит 2 дополнительные операции и логически никогда не может быть быстрее.
function sign(x) {
return typeof x === 'number' ? x ? x < 0 ? -1 : 1 : x === x ? 0 : NaN : NaN;
}
инструменты
- см. Этот тест jsperf выполнение тестов;
- скрипка - type-cast тесты;
улучшению приветствуются!
[Offtopic] принят ответ
Андрей Tarantsov - +100 ради искусства, но, к сожалению, это примерно в 5 раз медленнее, чем очевидный подход
Фредерик Хамиди - как-то самый популярный ответ (на время написания), и это здорово, но это определенно не то, как все должно быть сделано, имхо. Кроме того, он неправильно обрабатывает бесконечные числа, которые также являются числами, вы знаете.
kbec - это улучшение очевидного решения. Не то чтобы революционно, но все вместе я считаю этот подход лучшим. Голосуйте за него:)
14 ответов
деление числа на его абсолютное значение также дает его знак. Использование логического оператора и короткого замыкания позволяет нам использовать специальный случай 0
таким образом, мы не заканчиваем делением на него:
var sign = number && number / Math.abs(number);
функции вы ищете называется Сигнум, и лучший способ реализовать его:
function sgn(x) {
return (x > 0) - (x < 0);
}
не должно ли это поддерживать подписанные нули JavaScript (ECMAScript)? Кажется, что он работает при возврате x, а не 0 в функции "megafast":
function sign(x) {
return typeof x === 'number' ? x ? x < 0 ? -1 : 1 : x === x ? x : NaN : NaN;
}
это делает его совместимым с проектом математика ECMAScript.знак (MDN):
возвращает знак x, указывающий, является ли x положительным, отрицательным или нулевым.
- если x-NaN, то результат-NaN.
- если x равно -0, то результат is -0.
- если x равно +0, то результат равен +0.
- если x отрицательно, а не -0, результат равен -1.
- если x положительный, а не +0, результат равен +1.
для людей, которым интересно, что происходит с последними браузерами, в версии ES6 есть родной математика.знак метод. Вы можете проверить поддержка.
в основном он возвращает -1
, 1
, 0
или NaN
Math.sign(3); // 1
Math.sign(-3); // -1
Math.sign('-3'); // -1
Math.sign(0); // 0
Math.sign(-0); // -0
Math.sign(NaN); // NaN
Math.sign('foo'); // NaN
Math.sign(); // NaN
var sign = number >> 31 | -number >>> 31;
Superfast если вам не нужна бесконечность и вы знаете, что число является целым числом, найденным в источнике openjdk-7:java.lang.Integer.signum()
думаю, что это просто для удовольствия:
function sgn(x){
return 2*(x>0)-1;
}
0 и NaN вернет -1
отлично работает на + / - Infinity
решение, которое работает на всех числах, а также 0
и -0
, а также Infinity
и -Infinity
, является:
function sign( number ) {
return 1 / number > 0 ? 1 : -1;
}
на вопрос "а +0 и -0 то же самое?" для получения дополнительной информации.
предупреждение: ни один из этих ответов, включая теперь стандартный Math.sign
будет работать над делом 0
vs -0
. Это может не быть проблемой для вас, но в некоторых реализациях физики может вопрос.
вы можете немного сдвинуть число и проверить самый значительный бит (MSB). Если MSB является 1, то число отрицательно. Если это 0, то число является положительным (или 0).
Я как раз собирался задать тот же вопрос, но пришел к решению, прежде чем я закончил писать, увидел, что этот вопрос уже существует, но не видел этого решения.
(n >> 31) + (n > 0)
вроде бы быстрее, добавив троичной хотя (n >> 31) + (n>0?1:0)
очень похоже на ответ Martijn является
function sgn(x) {
isNaN(x) ? NaN : (x === 0 ? x : (x < 0 ? -1 : 1));
}
Я нахожу его более читабельным. Кроме того (или, в зависимости от вашей точки зрения, однако), он также groks вещи, которые могут быть интерпретированы как число; например, он возвращает -1
С '-5'
.
Я не вижу никакого практического смысла возвращения -0 и 0 от Math.sign
Итак, моя версия:
function sign(x) {
x = Number(x);
if (isNaN(x)) {
return NaN;
}
if (x === -Infinity || 1 / x < 0) {
return -1;
}
return 1;
};
sign(100); // 1
sign(-100); // -1
sign(0); // 1
sign(-0); // -1
методы, о которых я знаю, следующие:
математика.знак(Н)
var s = Math.sign(n)
это собственная функция, но медленнее всего из-за накладных расходов вызова функции. Однако он обрабатывает "NaN", где другие ниже могут просто принять 0 (т. е. математику.знак ("abc") - это NaN).
((n>0) - (n
var s = ((n>0) - (n<0));
в этом случае только левая или правая сторона может быть 1 на основе знак. Это приводит к либо 1-0
(1), 0-1
(-1), или 0-0
(0).
скорость этого кажется шея и шея со следующим ниже в Хроме.
(n>>31) / (!!n)
var s = (n>>31)|(!!n);
использует "арифметический сдвиг вправо". В основном сдвиг на 31 отбрасывает все биты, кроме знака. Если знак был установлен, это приводит к -1, в противном случае он равен 0. Право |
он проверяет на положительный, Преобразуя значение в boolean (0 или 1 [BTW: нечисловые строки, например !!'abc'
, в этом случае становится 0, а не NaN]) затем использует побитовую операцию или для объединения битов.
это, кажется, лучший в среднем производительность в браузерах (лучше всего в Chrome и Firefox, по крайней мере), но не самый быстрый во всех из них. По какой-то причине тернарный оператор быстрее в IE.
n?n
var s = n?n<0?-1:1:0;
самый быстрый в IE для некоторых причина.
см. Этот тест jsperf
выполненные тесты:https://jsperf.com/get-sign-from-value
мои два цента, с функцией, которая возвращает те же результаты, что и математика.знак будет делать, т. е. знак (-0) -- > -0, знак(-бесконечность) -- > - бесконечность, знак(null) -- > 0, знак(undefined) -- > NaN и т. д.
function sign(x) {
return +(x > -x) || (x && -1) || +x;
}
Jsperf не позволит мне создать тест или ревизию, извините, что не могу предоставить вам тесты (я дал jsbench.гитуб.io попытка, но результаты кажутся гораздо ближе друг к другу, чем с Jsperf...)
если кто-то может добавить его в редакцию Jsperf, я бы любопытно посмотреть, как он сравнивается со всеми ранее данными решениями...
спасибо!
Джим.
редактировать:
надо было написать:
function sign(x) {
return +(x > -x) || (+x && -1) || +x;
}
((+x && -1)
вместо (x && -1)
) для того чтобы обрабатывать sign('abc')
правильно (--> NaN)