ruby - Converting a floating point to its corresponding bit-segments -
given ruby float
value, e.g.,
f = 12.125
i'd wind 3-element array containing floating-point number's sign (1 bit), exponent (11 bits), , fraction (52 bits). (ruby's floats ieee 754 double-precision 64-bit representation.)
what's best way that? bit-level manipulation doesn't seem ruby's strong point.
note want bits, not numerical values correspond to. instance, getting [0, -127, 1]
floating-point value of 1.0
not i'm after -- want actual bits in string form or equivalent representation, ["0", "0ff", "000 0000 0000"]
.
the bit data can exposed via arrays pack
float doesn't 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 bit 'around houses' pull out string representation. i'm sure there more efficient way pull data original bytes
...
edit bit level manipulation tweaked interest had poke around. use operations in ruby need have integer float requires more unpack
ing convert 64 bit int. big endian/ieee754 documented representation trivial. little endian representation i'm not sure about. it's little odd, not on complete byte boundaries 11 bit exponent , 52 bit mantissa. it's becomes fiddly pull bits out , swap them resembles little endian, , not sure if it's right haven't seen reference layout. 64 bit value little endian, i'm not sure how applies components of 64bit value until store them somewhere else, 16bit int mantissa.
as example 11 bit value little > big, kind of thing doing shift significant byte left 3 front, or least significant 3 bits.
v = 0x4f2 ((v & 0xff) << 3) | ( v >> 8 ))
here anyway, of use.
class float float::little_endian = [1.0].pack("e") == [1.0].pack("d") # returns sign, exponent , mantissa integers def ieee745_binary64 # build big end int representation can use bit operations tb = [self].pack('d').unpack('q>').first # check if float::little_endian ieee745_binary64_little_endian tb else ieee745_binary64_big_endian tb end end # force little end calc def ieee745_binary64_little ieee745_binary64_little_endian [self].pack('e').unpack('q>').first end # force 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 # last 7 bits, make more significant exp_b = ( big_end_int & 0x8000 ) >> 15 # 9th bit, fill sign gap exp_c = ( big_end_int & 0x7000 ) >> 4 # 10-12th bit stick on front exponent = exp_a | exp_b | exp_c mant_a = ( big_end_int & 0xffffffffffff0000 ) >> 12 # f000 taken above mant_b = ( big_end_int & 0x0000000000000f00 ) >> 8 # f00 left on 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 |v| printer v, v.ieee745_binary64 printer v, v.ieee745_binary64_big end
til brain big endian! you'll note ints being worked both big endian. failed @ bit shifting other way.
Comments
Post a Comment