Search code examples
rubybindata

Implement a basic datatype in BinData


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}

Solution

  • (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:

    1. Subclass Primitive instead of Record
    2. Rename #to_f to #get
    3. Implement #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