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?
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:
- Let number be the result of calling ToNumber on the input argument. [This just converts non-numeric values like booleans and strings into numbers.]
- If number is NaN, +0, −0, +∞, or −∞, return +0.
- Let posInt be
sign(
number) * floor(abs(
number))
. [sign
returns -1 if number is negative or 1 if it's positive.]- 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.
- 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With