Number.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. Очевидный и быстрый

function sign(x) { return x > 0 ? 1 : x < 0 ? -1 : 0; }

1.1. Модификация от kbec - один тип, менее эффективный, более короткий [быстрый]

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. Искусство... но очень медленное [медленнее]

function sign(x) { return (x > 0) - (x < 0); }

4. Использование бит-сдвига
  быстро, но sign(-Infinity) -> 0

function sign(x) { return (x >> 31) + (x > 0 ? 1 : 0); }

5. Тип-безопасный [мегафаст]

! Кажется, что браузеры (особенно хром v8) делают некоторые магические оптимизации, и это решение оказывается намного более результативным, чем другие, даже чем (1.1), несмотря на то, что оно содержит 2 дополнительных операции и логически никогда не может быть быстрее.

function sign(x) {
    return typeof x === 'number' ? x ? x < 0 ? -1 : 1 : x === x ? 0 : NaN : NaN;
}

Инструменты

  • jsperf тесты соответствия,
  • fiddle - тесты типа-литья;

Усовершенствования приветствуются!


[Offtopic] Принятый ответ

  • Андрей Таранцов - +100 для искусства, но, к сожалению, он примерно в 5 раз медленнее, чем очевидный подход

  • Frédéric Hamidi - как-то самый лучший ответ (для написания времени), и это довольно круто, но определенно не так, как должно быть сделано, imho, Также он неправильно обрабатывает номера бесконечности, которые также являются числами.

  • kbec - это улучшение очевидного решения. Не то, что революционер, но, взяв все вместе, я считаю этот подход лучшим. Голосуйте за него:)

Ответ 1

Более элегантная версия быстрого решения:

var sign = number?number<0?-1:1:0

Ответ 2

Деление числа на его абсолютное значение также дает свой знак. Использование короткого замыкания логического оператора И позволяет нам делать особый случай 0, поэтому мы не делим его на него:

var sign = number && number / Math.abs(number);

Ответ 3

Функция, которую вы ищете, называется signum, и лучший способ ее реализовать:

function sgn(x) {
  return (x > 0) - (x < 0);
}

Ответ 4

Если это не поддерживает JavaScripts (ECMAScripts), подписанные нулями? Кажется, он работает при возврате x вместо 0 в функцию "мегафаст":

function sign(x) {
    return typeof x === 'number' ? x ? x < 0 ? -1 : 1 : x === x ? x : NaN : NaN;
}

Это делает его совместимым с черновиком ECMAScripts Math.sign (MDN):

Возвращает знак x, указывающий, является ли x положительным, отрицательным или нулевым.

  • Если x является NaN, результатом является NaN.
  • Если x равно -0, результат равен -0.
  • Если x равно +0, результат равен +0.
  • Если x отрицательно, а не -0, результат равен -1.
  • Если x положительно, а не +0, результат равен +1.

Ответ 5

Для тех, кому интересно, что происходит с последними браузерами, в версии ES6 существует собственный метод Math.sign. Здесь вы можете проверить поддержку .

В основном он возвращает -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

Ответ 6

var sign = number >> 31 | -number >>> 31;

Superfast, если вам не нужна бесконечность, и знайте, что число является целым числом, найденным в источнике openjdk-7: java.lang.Integer.signum()

Ответ 7

Я думал, что добавлю это просто для удовольствия:

function sgn(x){
  return 2*(x>0)-1;
}

0 и NaN вернет -1
отлично работает на +/- Бесконечность

Ответ 8

Решение, которое работает во всех числах, а также 0 и -0, а также Infinity и -Infinity:

function sign( number ) {
    return 1 / number > 0 ? 1 : -1;
}

Для получения дополнительной информации см. вопрос "+0 и -0 то же?".


Предупреждение: Ни один из этих ответов, включая стандартный Math.sign, не будет работать в случае 0 vs -0. Это может не быть проблемой для вас, но в некоторых реализациях физики это может иметь значение.

Ответ 9

Вы можете поменять число и проверить самый значительный бит (MSB). Если MSB равен 1, то число отрицательно. Если это 0, то число положительно (или 0).

Ответ 10

Я только собирался задать тот же вопрос, но пришел к решению, прежде чем я закончил писать, увидел, что этот вопрос уже существует, но не видел этого решения.

(n >> 31) + (n > 0)

кажется, быстрее, добавив тройной, хотя (n >> 31) + (n>0?1:0)

Ответ 11

Очень похоже на ответ Martijn

function sgn(x) {
    isNaN(x) ? NaN : (x === 0 ? x : (x < 0 ? -1 : 1));
}

Я считаю его более читаемым. Также (или, в зависимости от вашей точки зрения, однако), он также анализирует вещи, которые можно интерпретировать как число; например, он возвращает -1 при представлении '-5'.

Ответ 12

Я не вижу практического смысла возврата -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

Ответ 13

Методы, о которых я знаю, следующие:

Math.sign(п)

var s = Math.sign(n)

Это нативная функция, но самая медленная из-за накладных расходов на вызов функции. Однако он обрабатывает "NaN", где остальные ниже могут просто принять 0 (то есть Math.sign('abc') является NaN).

((n > 0) - (n < 0))

var s = ((n>0) - (n<0));

В этом случае только левая или правая сторона может быть 1 на основе знака. Это приводит либо к 1-0 (1), 0-1 (-1), либо к 0-0 (0).

Скорость этого кажется шеей и шеей, следующая ниже в Chrome.

(п → 31) | (!! п)

var s = (n>>31)|(!!n);

Использует "сдвиг смены знака". В основном сдвиг на 31 капля всех битов, кроме знака. Если знак был установлен, это приводит к -1, в противном случае оно равно 0. В правой части | он проверяет положительный результат, преобразовывая значение в значение boolean (0 или 1 [BTW: нечисловые строки, например !!'abc'), становятся 0 в этом случае, а не NaN]), затем использует побитовое ИЛИ для комбинирования бит.

Это, пожалуй, лучшая средняя производительность в браузерах (лучше всего в Chrome и Firefox), но не самая быстрая во всех из них. По какой-то причине тернарный оператор быстрее работает в IE.

п п < 0 -1:? 1: 0

var s = n?n<0?-1:1:0;

Самый быстрый в IE по какой-то причине.

JSPerf

Выполненные тесты: https://jsperf.com/get-sign-from-value

Ответ 14

Мои два цента с функцией, которая возвращает те же результаты, что и Math.sign, т.е. знак (-0) → -0, знак (-Infinity) → -Infinity, знак (null) → 0, знак (неопределенный) → NaN и т.д.

function sign(x) {
    return +(x > -x) || (x && -1) || +x;
}

Jsperf не позволит мне создать тест или версию, извините за невозможность предоставить вам тесты (я дал jsbench.github.io попытку, но результаты кажутся намного ближе друг к другу, чем с Jsperf...)

Если бы кто-то мог добавить его в ревизию Jsperf, мне было бы интересно узнать, как он сравнивается со всеми ранее предоставленными решениями...

Спасибо!

Джим.

EDIT:

Я должен был написать:

function sign(x) {
    return +(x > -x) || (+x && -1) || +x;
}

((+x && -1) вместо (x && -1)) для правильной обработки sign('abc') (-> NaN)