I am trying to implement binary16 encoding for a half-precision floating point type.
The code is working except for one detail: It returns an object with the three properties (sign, exponent, fraction), but I would like it to return the float. Right now, I have to call to_f
to get to the float. I would like this to work the way the integrated int and float classes work.
Here's my code:
require 'bindata'
class Binary16Be < BinData::Record
# naming based on https://en.wikipedia.org/wiki/Half-precision_floating-point_format
bit1 :sign_bit
bit5 :exponent
bit10 :fraction
def sign
sign_bit.zero? ? 1 : -1
end
def to_f
if exponent == 31 # special value in binary16 - all exponent bits are 1
return fraction.zero? ? (sign * Float::INFINITY) : Float::NAN
end
sign * 2**(exponent - 15) * (1.0 + fraction.to_f / 1024)
end
end
What I would like:
Binary16Be.read("\x3C\x00")
=> 1.0
What happens right now:
Binary16Be.read("\x3C\x00")
{:sign_bit=>0, :exponent=>15, :fraction=>0}
(this is not actually my own answer, I received this from the gem's author. Made slight alterations to his answer to fit this Q&A format a little better.)
The procedure is described in the bindata Wiki / Primitive Types
In your case:
Primitive
instead of Record
#to_f
to #get
#set
The converted code
class Binary16Be < BinData::Primitive
# naming based on
# https://en.wikipedia.org/wiki/Half-precision_floating-point_format
bit1 :sign_bit
bit5 :exponent
bit10 :fraction
def sign
sign_bit.zero? ? 1 : -1
end
def get
if exponent == 31 # special value in binary16 - all exponent bits are 1
return fraction.zero? ? (sign * Float::INFINITY) : Float::NAN
end
sign * 2**(exponent - 15) * (1.0 + fraction.to_f / 1024)
end
def set(val)
self.sign = (val >= 0.0)
self.fraction = ... # TODO implement
self.exponent = ... # TODO implement
end
end