Convert a floating point to its corresponding bit segments

Given the value of Ruby Float , for example,

 f = 12.125 

I would like to run a three element array containing the sign of a floating point number (1 bit), an exponent (11 bits) and a fraction (52 bits). (Ruby float is a 64-bit double-precision representation of IEEE 754.)

What is the best way to do this? Bit-level manipulation doesn't seem to be Ruby's forte.

Note that I need bits, not the numeric values ​​that they correspond to. For example, getting [0, -127, 1] for a floating point value of 1.0 not what I need - I want the actual bits in the form of a string or equivalent representation, for example ["0", "0ff", "000 0000 0000"] .

+5
source share
2 answers

Bit data can be represented through the Arrays pack , as Float does not provide functions internally.

 str = [12.125].pack('D').bytes.reverse.map{|n| "%08b" %n }.join => "0100000000101000010000000000000000000000000000000000000000000000" [ str[0], str[1..11], str[12..63] ] => ["0", "10000000010", "1000010000000000000000000000000000000000000000000000"] 

This is a little “around the house” to get it out of the string representation. I am sure there is a more efficient way to pull data from the original bytes ...


Change Bit level manipulation changed my interest, so I had a dream. To use operations in Ruby, you need to have Integer, so float requires a little more unpack ing to convert to a 64-bit int. The big end documentary / ieee 754 is pretty trivial. A little idea of ​​the end I'm not so sure. This is a bit strange since you are not at full byte boundaries with an 11-bit metric and a 52-bit mantissa. It becomes hesitant to pull out the bits and swap them to get what looks like a little endian, and not sure if this is correct, since I have not seen any reference to the layout. Thus, the 64-bit value is little endian, I'm not too sure how this applies to the components of the 64-bit value until you save them in another place, for example, 16 bits of int for the mantissa.

As an example for an 11-bit value from little> big, the kind of thing I did was to move the most significant byte from left 3 to the front, then to OR with the least significant 3 bits.

 v = 0x4F2 ((v & 0xFF) << 3) | ( v >> 8 )) 

Anyway, I hope its use.

 class Float Float::LITTLE_ENDIAN = [1.0].pack("E") == [1.0].pack("D") # Returns a sign, exponent and mantissa as integers def ieee745_binary64 # Build a big end int representation so we can use bit operations tb = [self].pack('D').unpack('Q>').first # Check what we are if Float::LITTLE_ENDIAN ieee745_binary64_little_endian tb else ieee745_binary64_big_endian tb end end # Force a little end calc def ieee745_binary64_little ieee745_binary64_little_endian [self].pack('E').unpack('Q>').first end # Force a big end calc def ieee745_binary64_big ieee745_binary64_big_endian [self].pack('G').unpack('Q>').first end # Little def ieee745_binary64_little_endian big_end_int #puts "big #{big_end_int.to_s(2)}" sign = ( big_end_int & 0x80 ) >> 7 exp_a = ( big_end_int & 0x7F ) << 1 # get the last 7 bits, make it more significant exp_b = ( big_end_int & 0x8000 ) >> 15 # get the 9th bit, to fill the sign gap exp_c = ( big_end_int & 0x7000 ) >> 4 # get the 10-12th bit to stick on the front exponent = exp_a | exp_b | exp_c mant_a = ( big_end_int & 0xFFFFFFFFFFFF0000 ) >> 12 # F000 was taken above mant_b = ( big_end_int & 0x0000000000000F00 ) >> 8 # F00 was left over mantissa = mant_a | mant_b [ sign, exponent, mantissa ] end # Big def ieee745_binary64_big_endian big_end_int sign = ( big_end_int & 0x8000000000000000 ) >> 63 exponent = ( big_end_int & 0x7FF0000000000000 ) >> 52 mantissa = ( big_end_int & 0x000FFFFFFFFFFFFF ) >> 0 [ sign, exponent, mantissa ] end end 

and testing ...

 def printer val, vals printf "%-15s sign|%01b|\n", val, vals[0] printf " hex e|%3x| m|%013x|\n", vals[1], vals[2] printf " bin e|%011b| m|%052b|\n\n", vals[1], vals[2] end floats = [ 12.125, -12.125, 1.0/3, -1.0/3, 1.0, -1.0, 1.131313131313, -1.131313131313 ] floats.each do |v| printer v, v.ieee745_binary64 printer v, v.ieee745_binary64_big end 

TIL my brain is big andian! You will notice that the ints that it works with have a large endian. I failed by moving a little the other way.

+4
source

Use frexp from the Math module. From doc :

 fraction, exponent = Math.frexp(1234) #=> [0.6025390625, 11] fraction * 2**exponent #=> 1234.0 

The bit sign is easy to find on your own.

+3
source

Source: https://habr.com/ru/post/1202256/


All Articles