Ruby: convert integer to 32 bit binary number (or string) - ruby

I want to convert a number, say 1, into a 32 bit binary number:
00000000000000000000000000000001
how can I do this to ensure the full string is of length 32, no matter how small the number might be?
I had a sprintf working for 8 bit binary, but not sure how to make it 32.

Use String#rjust:
1.to_s(2).rjust(32, '0')
#⇒ "00000000000000000000000000000001"

String#% (via sprintf):
'%032b' % 7
=> "00000000000000000000000000000111"

Using pack and unpack1:
[1].pack('L>').unpack1('B*')
#=> "00000000000000000000000000000001"
L indicates 32-bit unsigned integer, > denotes big endian. B indicates bit string, * outputs all available bits.
This will wrap around when exceeding the 32-bit unsigned integer range:
[4_294_967_294].pack('L>').unpack1('B*') #=> "11111111111111111111111111111110"
[4_294_967_295].pack('L>').unpack1('B*') #=> "11111111111111111111111111111111"
[4_294_967_296].pack('L>').unpack1('B*') #=> "00000000000000000000000000000000"
[4_294_967_297].pack('L>').unpack1('B*') #=> "00000000000000000000000000000001"

Related

More elegant way to convert 4 bytes to 32bit integer

I'm converting arrays consisting of four byte values to 32bit numbers by executing the following code:
a = [0, 16, 82, 0]
i = a.map { |e| "%02x" % e }.join.to_i(16)
# => 1069568
It works as intended, but I wonder if there's a more elegant way to perform this task. Maybe not utilizing strings.
Using pack and unpack1:
a = [0, 16, 82, 0]
a.pack('C4').unpack1('L>')
#=> 1069568
C4 means 8-bit unsigned (4 times) and L> means 32-bit unsigned (big endian).
However, pack returns a binary string, so this is not string-free.
If you have one byte, that would be the result as is. If you add one byte to the right side, that would make the original result move two positions to the left (which means multiplying by 0x100, or 16 ** 2 = 256), and add the new byte. You can repeat this as many times as there are bytes.
a.inject{|acc, byte| acc * 0x100 + byte}
# => 1069568

How to convert in to signed int (4byte) using Ruby

I need to convert the follow 4 byte integer to signed integer, like:
input 65535 value -1
input 65534 value -2
input 65533 value -3
and so on...
I tried the follow:
puts (65533).to_s(16) #=> fffd
puts (65533).to_s(16).unpack('s') #=> doesn't work properly... return 26214
Can someone help me with code above?
Best Regards
You could pack it as as an unsigned integer and then unpack it as a signed integer:
[65535, 65534, 65533].pack('S*').unpack('s*')
#=> [-1, -2, -3]
S / s denote 16-bit integers, you can also use L / l for 32-bit or Q / q for 64-bit.

How to avoid octal, binary, etc. numbers changing output during string conversion

I'm working on a codewars kata that uses the Luhn Algorithm
to validate credit card numbers. Already have my answer, which uses string conversion to split the digits and then reconvert to an integer and then use the rest of the algorithm. All well and good until I test it on an octal number.
The code starts as:
def validate(n)
n.to_s.split("") #n = 7867 gives me ["7","8","6","7"], which is fine
n.to_s.split("") #n = 0776 gives me ["5","1","0], when I need ["0","7","7","6"]
n.to_s.split("") #n = 0100 gives me ["6", "4"] when I need ["0","1","0","0"]
#other code here
end
where the method should be called on whatever n is plugged in.
How do I prevent an octal, binary, or hexidecimal, etc number from converting like that? Is there a way to keep the digits as is so I can use them?
Your problem is that you are obviously storing the number as a number, in n; or else you would not need to say n.to_s.
There must be a place where those numbers enter your program; a user prompt, or by reading a file or whatever. You likely have something like n = user_input.to_i somewhere, forcing ruby to convert the string to a number. Make sure that you treat n as string everywhere, and leading zeroes will stick around.
If you are using numeric literals in your program, you can explicitly say that they are decimal:
n = 0d0100 # or "0100".to_i
n.to_s.rjust(4,"0").split("")
#=> ["0", "1", "0", "0"]
Also, use rjust to make them 4 digits long in string form as credit/debit cards have four 4-digit numbers
To split credit card into an array of integer, you could do something like below:
cc = "4111 1111 1111 1111".gsub(/ /,"").split("").map(&:to_i)
#=> [4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

Reimplementing to_i with pack/unpack

I re-implemented String#to_i to use bytes to store individual characters. My solution is:
def to_i(string)
string.bytes.map { |ascii| ascii - 48 }.inject { |sum, int| sum * 10 + int}
end
to_i("22") # => 22
I was wondering if it was possible to implement to_i with pack and unpack instead.
It seems the only thing pack/unpack can do here is convert the digit string into array of numbers. Besides, the implementation is buggy. It didn't check whether the input is really digit string.
def to_i(string)
string.unpack('C*').reduce(0){|sum, i| sum * 10 + i - 48}
end

How to store a 64 bit integer in two 32 bit integers in Ruby

As the title says, I'm a little lost on how to accomplish this in Ruby...there are number of topics on how to do this in C or C++. Any ruby experts out there that can chime in on this?
The same syntax you'd use in C works in ruby, just drop the typecasts:
n = 0xFFFFFFFFEEEEEEEE
x = (n & 0xFFFFFFFF00000000) >> 32
y = n & 0xFFFFFFFF
puts x.to_s(16)
# => "ffffffff"
puts y.to_s(16)
# => "eeeeeeee"
v = x << 32 | y
puts v.to_s(16)
# => "ffffffffeeeeeeee"
If you need the values to be in chunks of exactly 32 bits (i.e. you need to speak binary to some external data file or program), then you'll want to use Array#pack and String#unpack to get the right bits.
one 64bit integer is not equal to two 32bit integers.
http://en.wikipedia.org/wiki/Integer_(computer_science)

Resources