Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to truncate large integers during bitwise operations to mimic JavaScript in Ruby?

I've found that working with integers in Ruby causes them to act different than JS when their binary representation is larger than 32 bits.

a = 144419633058839139324
b = 3903086624

JS:

a >> 0; => 1482555392
b >> 0; => -391880672

Ruby:

a >> 0 => 144419633058839139324
[a].pack('V').unpack('V').first => 1482560508
[b].pack('V').unpack('V').first => 3903086624

My question is how can I convert my Ruby code to give the same return values JS?

like image 629
Travis Avatar asked Oct 06 '14 02:10

Travis


1 Answers

This is a great question. I did some experimentation trying to figure out exactly what operations JavaScript is doing, but then I thought, "I bet the spec says." Sure enough, it did!

First I checked out the Bitwise Shift Operators section, and what I learned from that is what you already knew: Before doing bitwise operations, JavaScript converts its operands to 32-bit integers. For that, it links to the definition of an "abstract operation" (i.e. an algorithm to be implemented by JavaScript engines) called ToInt32. Happily, it's really easy to follow:

ToInt32: (Signed 32 Bit Integer)

The abstract operation ToInt32 converts its argument to one of 232 integer values in the range -231 through 231−1, inclusive. This abstract operation functions as follows:

  1. Let number be the result of calling ToNumber on the input argument. [This just converts non-numeric values like booleans and strings into numbers.]
  2. If number is NaN, +0, −0, +∞, or −∞, return +0.
  3. Let posInt be sign(number) * floor(abs(number)). [sign returns -1 if number is negative or 1 if it's positive.]
  4. Let int32bit be posInt modulo 232; that is, a finite integer value k of Number type with positive sign and less than 232 in magnitude such that the mathematical difference of posInt and k is mathematically an integer multiple of 232.
  5. If int32bit is greater than or equal to 231, return int32bit − 232, otherwise return int32bit.

We can translate this directly into Ruby (I've numbered the steps 1–5 as comments):

def to_int32(number)
  # (1)(2)
  begin
    sign = number < 0 ? -1 : 1
    abs = number.abs
    return 0 if abs == 0 || abs == Float::INFINITY
  rescue
    return 0
  end

  pos_int = sign * abs.floor  # (3)
  int_32bit = pos_int % 2**32 # (4)

  # (5)
  return int_32bit - 2**32 if int_32bit >= 2**31
  int_32bit
end

So, does it work?

a = 144419633058839130000
puts to_int32(a)
# => 1482551184

b = 3903086624
puts to_int32(b)
# => -391880672

Seems legit!

Now, I'm sure there are more concise and probably faster ways to do this, but this should get you started.

like image 75
Jordan Running Avatar answered Oct 11 '22 17:10

Jordan Running