4

I've been looking all over to find if there is a method that converts a float number(ex: 123.456) into a binary32. I've found a lot of solutions that go from binary32 to float, but not vice versa.

5
  • possible duplicate of How to convert binary32 to float in ruby Commented Jul 18, 2013 at 19:51
  • I saw that post, but no clue how it works. I feel like its because everything in ruby is an object, I need to somehow obtain real floating number. Commented Jul 18, 2013 at 20:02
  • 1
    pack and unpack are useful for converting between binary formats. It doesn't matter that everything's an object, even floating point numbers. To reverse the process of an unpack you usually just call pack with the same specification. Commented Jul 18, 2013 at 20:04
  • Did you try [123.456].pack('e')? Exactly what format of output are you looking for? Commented Jul 18, 2013 at 20:08
  • I want to convert 123.456 to 01000010111101101110100101111001. [123.456].pack('e') gave me a funky return "y\xE9\xF6B" Commented Jul 18, 2013 at 20:26

3 Answers 3

8

The "funky" y\xE9\xF6B value is the actual binary value represented as a string.

If you want to convert that to a string representation of the binary value:

"%032b" % [123.456].pack('e').reverse.each_char.inject(0) { |sum,c| sum = (sum << 8) + c.ord }
=> "01000010111101101110100101111001"

So breaking it down, this gives you the "funky" binary value packed into a string:

[123.456].pack('e')

The rest converts the "binary string" into a an integer (the appropriate binary digits of the float "casted" to integer):

reverse               # Handles the endian-ness
each_char             # Grabs each character in the reversed string
inject                # Concatenates the chars converted directly to binary

And then the "%032b" % displays it as a binary string so you can look at it.

EDIT: As @looby astutely observed, 'g' can be used in pack instead of 'e' to avoid the reverse step, shortening this solution to: [123.456].pack('g').each_char.inject(0) { |sum,c| sum = (sum << 8) + c.ord } (and use "%032b" %... as before in order to display it).

Sign up to request clarification or add additional context in comments.

Comments

6

The above is awesome but I have a few simplifications:

[123.456].pack('g').bytes.map{|n| "%08b" % n}.join

Using the 'g' flag instead of 'e' avoids having to reverse the output from pack. The bytes method does the same thing as calling .ord on each character does. Then instead of taking the 4 integers and doing the sum/bit shift you map each to an 8 character binary string and join them together.

1 Comment

The g option is a great catch and does get rid of the reverse. However, the solution you're showing doesn't have, as an intermediate value, the entire binary value I thought @TakaGoto desired (versus just the ASCII binary string). that's what the inject does in the solution I proposed. So the solution that outputs binary would be [123.456].pack('g').each_char.inject(0) { |sum,c| sum = (sum << 8) + c.ord }.
3

You should use String#unpack and Array#pack :

[123.456].pack('g').unpack('B*').first
#=> "01000010111101101110100101111001"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.