Использование побитового ИЛИ 0 для создания номера - PullRequest
174 голосов
/ 20 сентября 2011

Мой коллега наткнулся на метод для вычисления чисел с плавающей запятой, используя поразрядно или:

var a = 13.6 | 0; //a == 13

Мы говорили об этом и удивлялись нескольким вещам.

  • Как это работает? Наша теория состояла в том, что использование такого оператора приводит число к целому числу, удаляя дробную часть
  • Есть ли у него какие-либо преимущества перед Math.floor? Может быть, это немного быстрее? (каламбур не предназначен)
  • Есть ли у него недостатки? Может быть, это не работает в некоторых случаях? Ясность очевидна, так как мы должны были это выяснить, и я пишу этот вопрос.

Спасибо.

Ответы [ 6 ]

137 голосов
/ 20 сентября 2011

Как это работает?Наша теория заключалась в том, что при использовании такого оператора число преобразуется в целое число, что приводит к удалению дробной части

Все побитовые операции, кроме беззнакового сдвига вправо, >>>, работают со знаковыми 32-разрядными целыми числами.Поэтому использование побитовых операций преобразует число с плавающей точкой в ​​целое число.

Есть ли у него какие-либо преимущества по сравнению с Math.floor?Может быть, это немного быстрее?(каламбур не предназначен)

http://jsperf.com/or-vs-floor/2 кажется немного быстрее

Есть ли у него какие-либо недостатки?Может быть, это не работает в некоторых случаях?Ясность очевидна, так как нам пришлось это выяснить, и я пишу этот вопрос.

  • Не передаст jsLint.
  • Только 32-разрядные целые числа со знаком
  • Нечетное Сравнительное поведение: Math.floor(NaN) === NaN, тогда как (NaN | 0) === 0
28 голосов
/ 20 сентября 2011

Это усечение в отличие от настила. Ответ Ховарда вроде правильный; Но я бы добавил, что Math.floor делает именно то, что положено в отношении отрицательных чисел. Математически это и есть пол.

В случае, который вы описали выше, программист больше интересовался усечением или полным отключением десятичной дроби. Хотя синтаксис, который они использовали, скрывает тот факт, что они конвертируют число с плавающей точкой в ​​int.

17 голосов
/ 10 января 2016

В ECMAScript 6 эквивалент |0 равен Math.trunc , что я должен сказать:

Возвращает неотъемлемую часть числа, удаляя все дробныецифры.Он просто усекает точку и цифры за ней, независимо от того, является ли аргумент положительным числом или отрицательным числом.

Math.trunc(13.37)   // 13
Math.trunc(42.84)   // 42
Math.trunc(0.123)   //  0
Math.trunc(-0.123)  // -0
Math.trunc("-1.123")// -1
Math.trunc(NaN)     // NaN
Math.trunc("foo")   // NaN
Math.trunc()        // NaN
10 голосов
/ 20 сентября 2011

Ваш первый пункт верен.Число приводится к целому числу, и поэтому любые десятичные цифры удаляются.Обратите внимание, что Math.floor округляет до следующего целого числа в сторону минус бесконечность и, таким образом, дает другой результат применительно к отрицательным числам.

5 голосов
/ 20 сентября 2011
  • В спецификациях говорится, что оно конвертируется в целое число:

    Пусть lnum будет ToInt32 (lval).

  • Производительность: это было проверено на jsperf раньше.

примечание: удалена мертвая ссылка на спецификацию

2 голосов
/ 04 октября 2018

Javascript представляет Number как 64-битные числа с плавающей запятой двойной точности .

Math.floor работает с учетом этого.

Битовые операции работают в 32-битных подписано целых чисел.32-разрядные целые числа со знаком используют первый бит в качестве отрицательного значения, а остальные 31 бит - это число.Из-за этого допустимые минимальные и максимальные числа 32-битных чисел со знаком равны -2,147,483,648 и 2147483647 (0x7FFFFFFFF) соответственно.

Так что, когда вы делаете | 0, вы по сути делаете & 0xFFFFFFFF.Это означает, что любое число, представленное как 0x80000000 (2147483648) или более, будет возвращено как отрицательное число.

Например:

 // Safe
 (2147483647.5918 & 0xFFFFFFFF) ===  2147483647
 (2147483647      & 0xFFFFFFFF) ===  2147483647
 (200.59082098    & 0xFFFFFFFF) ===  200
 (0X7FFFFFFF      & 0xFFFFFFFF) ===  0X7FFFFFFF

 // Unsafe
 (2147483648      & 0xFFFFFFFF) === -2147483648
 (-2147483649     & 0xFFFFFFFF) ===  2147483647
 (0x80000000      & 0xFFFFFFFF) === -2147483648
 (3000000000.5    & 0xFFFFFFFF) === -1294967296

Также.Побитовые операции не "пол".Они усекают , что равносильно тому, что говорят, они округляются ближе всего к 0.Как только вы переходите к отрицательным числам, Math.floor округляет вниз , а поразрядно начинает округлять вверх .

Как я уже говорил, Math.floor безопаснее, потому что он работаетс 64-битными плавающими числами.Побитовый быстрее , да, но ограничен 32-битной областью со знаком.

Подводя итог:

  • Битовый режим работает так же, если вы работаете с 0 to 2147483647.
  • Битовый код отключен на 1 число, если вы работаете с -2147483647 to 0.
  • Битовое различие полностью отличается для чисел меньше -2147483648 и больше 2147483647.

Если вы действительно хотите настроить производительность и использовать оба:

function floor(n) {
    if (n >= 0 && n < 0x80000000) {
      return n & 0xFFFFFFFF;
    }
    if (n > -0x80000000 && n < 0) {
      return (n - 1) & 0xFFFFFFFF;
    }
    return Math.floor(n);
}

Просто добавить Math.trunc работает как побитовые операции.Таким образом, вы можете сделать это:

function trunc(n) {
    if (n > -0x80000000 && n < 0x80000000) {
      return n & 0xFFFFFFFF;
    }
    return Math.trunc(n);
}
...