Использование побитового ИЛИ 0 для создания номера

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

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

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

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

Благодарю.

7 ответов

Решение

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

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

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

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

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

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

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

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

В 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

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

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

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

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

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

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

      var myNegInt = -1 * Math.pow(2, 32);
var myFloat = 0.010203040506070809;
var my64BitFloat = myNegInt - myFloat;
var trunc1 = my64BitFloat | 0;
var trunc2 = ~~my64BitFloat;
var trunc3 = my64BitFloat ^ 0;
var trunc4 = my64BitFloat - my64BitFloat % 1;
var trunc5 = parseInt(my64BitFloat);
var trunc6 = Math.floor(my64BitFloat);
console.info(my64BitFloat);
console.info(trunc1);
console.info(trunc2);
console.info(trunc3);
console.info(trunc4);
console.info(trunc5);
console.info(trunc6);

IMO: На вопрос «Как это работает?», «Есть ли у него какие-то преимущества перед Math.floor?», «Есть ли у него недостатки?» бледный по сравнению с "Логично ли вообще использовать его для этой цели?"

Я думаю, что перед тем, как вы начнете разбираться в своем коде, вы можете запустить их. Мой совет; просто двигайтесь, здесь не на что смотреть. Побитовое использование для сохранения нескольких операций, и если это вообще важно для вас, обычно означает, что ваша архитектура кода требует доработки. Что касается того, почему это может иногда работать , остановленные часы показывают точность дважды в день, что не делает их полезными. У этих операторов есть свои применения, но не в этом контексте.

Другие вопросы по тегам