Using bitwise OR 0 to floor a number

前端 未结 6 1837
抹茶落季
抹茶落季 2020-11-22 05:01

A colleague of mine stumbled upon a method to floor float numbers using a bitwise or:

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

We were talking about it

6条回答
  •  一个人的身影
    2020-11-22 05:51

    Javascript represents Number as Double Precision 64-bit Floating numbers.

    Math.floor works with this in mind.

    Bitwise operations work in 32bit signed integers. 32bit signed integers use first bit as negative signifier and the other 31 bits are the number. Because of this, the min and max number allowed 32bit signed numbers are -2,147,483,648 and 2147483647 (0x7FFFFFFFF), respectively.

    So when you're doing | 0, you're essentially doing is & 0xFFFFFFFF. This means, any number that is represented as 0x80000000 (2147483648) or greater will return as a negative number.

    For example:

     // 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
    

    Also. Bitwise operations don't "floor". They truncate, which is the same as saying, they round closest to 0. Once you go around to negative numbers, Math.floor rounds down while bitwise start rounding up.

    As I said before, Math.floor is safer because it operates with 64bit floating numbers. Bitwise is faster, yes, but limited to 32bit signed scope.

    To summarize:

    • Bitwise works the same if you work from 0 to 2147483647.
    • Bitwise is 1 number off if you work from -2147483647 to 0.
    • Bitwise is completely different for numbers less than -2147483648 and greater than 2147483647.

    If you really want to tweak performance and use both:

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

    Just to add Math.trunc works like bitwise operations. So you can do this:

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

提交回复
热议问题