I dati di bit possono essere esposti tramite le matrici pack
poiché Float non fornisce funzioni internamente.
str = [12.125].pack('D').bytes.reverse.map{|n| "%08b" %n }.join
=> "0100000000101000010000000000000000000000000000000000000000000000"
[ str[0], str[1..11], str[12..63] ]
=> ["0", "10000000010", "1000010000000000000000000000000000000000000000000000"]
Questo è un po '"intorno alle case" per estrarlo da una rappresentazione di stringa. Sono sicuro che ci sia un modo più efficiente per estrarre i dati dal originale bytes
...
Modifica La manipolazione livello di bit ottimizzato il mio interesse così ho avuto un poke intorno. Per utilizzare le operazioni in Ruby è necessario avere un numero intero in modo che il float richieda ancora un po 'di unpack
per convertire in un int di 64 bit. La rappresentazione documentata di big endian/ieee754 è abbastanza banale. La rappresentazione little endian di cui non sono così sicuro. È un po 'strano, dato che non ci sono limiti di byte completi con un esponente a 11 bit e mantissa a 52 bit. È troppo difficile estrarre i bit e scambiarli per ottenere ciò che somiglia a little endian, e non è sicuro se sia giusto perché non ho visto alcun riferimento al layout. Quindi il valore a 64 bit è little endian, non sono troppo sicuro di come ciò si applichi ai componenti del valore a 64 bit finché non li si memorizza da qualche altra parte, come un int a 16 bit per la mantissa.
Come esempio per un valore a 11 bit da piccolo> grande, il tipo di cosa che stavo facendo era spostare il byte più significativo rimasto 3 in primo piano, quindi OR con i 3 bit meno significativi.
v = 0x4F2
((v & 0xFF) << 3) | (v >> 8))
Qui è comunque, si spera sia di qualche utilità.
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
e test ...
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 mio cervello è big endian! Noterai che gli int che stanno lavorando sono entrambi big endian. Ho fallito un po 'spostando l'altro modo.
John, grazie per la menzione della rappresentazione [IEEE 754] (http://en.wikipedia.org/wiki/IEEE_floating_point) di float. Non ero conscio di ciò.Se qualche lettore ha bisogno di un promemoria, viene fornito un esempio di calcolo [qui] (http://class.ece.iastate.edu/arun/cpre381/ieee754/ie4.html). –