lib/float-formats/classes.rb in float-formats-0.2.1 vs lib/float-formats/classes.rb in float-formats-0.3.0

- old
+ new

@@ -33,13 +33,12 @@ # * with gradual underflow: the minimium encoded exponent (0) is used for zero and denormal numbers; # i.e. the minimum encoded exponent implies the hidden bit is 0 # * minimum encoded exponent reserved for zero (nonzero significands are not used with it) # * special all zeros representation: minimun encoded exponent is a regular exponent except for 0 -require 'nio' -require 'nio/sugar' require 'flt' +require 'numerals' require 'enumerator' require 'float-formats/bytes.rb' module Flt @@ -89,91 +88,83 @@ v = form_class.send(*args) end @sign,@significand,@exponent = v.to_a end end + attr_reader :sign, :significand, :exponent + def to_a split end + def split return [@sign,@significand,@exponent] end - def nan? @exponent == :nan end + def zero? return @exponent==:zero || @significand==0 end + def infinite? return @exponent==:infinity end + def subnormal? return @exponent==:denormal || (@significand.kind_of?(Integer) && @significand<self.class.minimum_normalized_integral_significand) end + def normal? @exponend.kind_of?(Integer) && @significand>=self.class.minimum_normalized_integral_significand end - include Nio::Formattable - # from/to integral_sign_significand_exponent - def nio_write_neutral(fmt) - # this always treats the floating-point values as exact quantities - case @exponent - when :zero - v = 0.0/@sign - when :infinity - v = @sign/0.0 - when :nan - v = 0.0/0.0 - else - case form_class.radix - when 10 - v = Flt.DecNum(@sign, @significand, @exponent) - when 2 - v = Flt.BinNum(@sign, @significand, @exponent) - else - v = @significand*form_class.radix_power(@exponent)*@sign - end + def to_text(fmt = Numerals::Format[]) + if infinite? + fmt = fmt[symbols: [show_plus: true]] end - v.nio_write_neutral(fmt) + fmt.write(self) end - def to_text(fmt=Nio::Fmt.default) - nio_write(fmt) - end + def to_bytes form_class.pack(@sign,@significand,@exponent) end def to_hex(sep_bytes=false) if (form_class.total_bits % 8)==0 to_bytes.to_hex(sep_bytes) else to_bits.to_hex end end - def to(number_class, mode=:approx) - if number_class==Bytes + def to(number_class, mode = :approx) + if number_class == Bytes to_bytes - elsif number_class==String - mode = Nio::Fmt.default if mode==:approx + elsif number_class == String + mode = Numerals::Format[] if mode == :approx to_text(mode) - elsif Nio::Fmt===number_class + elsif Numerals::Format === number_class to_text(number_class) - elsif number_class==Array + elsif number_class == Array split - elsif Symbol===number_class + elsif Symbol === number_class send "to_#{number_class}" elsif number_class.is_a?(Flt::Num) && number_class.radix == form_class.radix self.to_num + elsif number_class.is_a?(FormatBase) + number_class.from_number(self, mode) else # assume number_class.ancestors.include?(Numeric) (number_class < Numeric) - fmt = mode==:approx ? Nio::Fmt::CONV_FMT : Nio::Fmt::CONV_FMT_STRICT - v = nio_write(fmt) - number_class.nio_read(v,fmt) + options = { + type: number_class, + exact_input: (mode != :approx), + output_mode: :fixed + } + Numerals::Conversions.convert(self, options) end end def to_bits to_bytes.to_bits(form_class.endianness,false,form_class.total_bits) end @@ -181,30 +172,29 @@ # as a text representation in a given base. # Accepts either a Value or a byte String. def to_bits_text(base) to_bits.to_s(base) #i = to_bits - #fmt = Nio::Fmt.default.base(base,true).mode(:fix,:exact) + #fmt = Numerals::Format[base: base] #if [2,4,8,16].include?(base) # n = (Math.log(base)/Math.log(2)).round.to_i # digits = (form_class.total_bits+n-1)/n - # fmt.pad0s!(digits) + # fmt.set_trailing_zeros!(digits) #end - #i.to_i.nio_write(fmt) + #fmt.writ(i.to_i) end # Computes the negation of a floating point value (unary minus) def minus form_class.new(-@sign,@significand,@exponent) end # Converts a floating point value to another format - def convert_to(fpclass) - fpclass.nio_read(nio_write) + def convert_to(fpclass, options = {}) + Numerals::Conversions.convert(self, options.merge(type: fpclass)) end - # Computes the next adjacent floating point value. # Accepts either a Value or a byte String. # Returns a Value. # TODO: use Flt def next_plus @@ -382,11 +372,11 @@ if @denormal_encoded_exp>=@min_encoded_exp @min_encoded_exp = @denormal_encoded_exp # originally, we incremented min_encoded_exp here unconditionally, but # now we don't if there's no hidden bit # (we assume the minimum exponent can be used for normalized and denormalized numbers) - # because of this, IEEE_EXTENDED & 128 formats now specify :min_encoded_exp=>1 in it's definitions + # because of this, IEEE_EXTENDED & 128 formats now specify min_encoded_exp: 1 in it's definitions @min_encoded_exp += 1 if @hidden_bit end end # Note that if there's a hidden bit and no gradual underflow, the minimum encoded exponent will only # be used for zero unless a parameter :min_encoded_exp (=0) is passed. In this case all numbers with @@ -578,27 +568,64 @@ def self.num_class Num[self.radix] end + def self.numerals_conversion(options = {}) + FltFmtConversion.new(self, options) + end + def self.context num_class::Context.new( - :precision=>significand_digits, - :emin=>radix_min_exp(:scientific_significand), - :emax=>radix_max_exp(:scientific_significand), - :rounding=>@round || :half_even + precision: significand_digits, + emin: radix_min_exp(:scientific_significand), + emax: radix_max_exp(:scientific_significand), + rounding:@round || :half_even ) end def to_num - s,c,e = split - e = 0 if e == :zero - form_class.num_class.Num(s,c,e) + s, c, e = split + case e + when :zero + e = 0 + when :infinity + e = :inf + when :nan + e = :nan + end + form_class.num_class.Num(s, c, e) end + # def to_num + # # num_class = Flt::Num[form_class.radix] + # num_class = self.class.num_class + # case @exponent + # when :zero + # num_class.zero(@sign) + # when :infinity + # num_class.infinity(@sign) + # when :nan + # num_class.nan + # else + # num_class.new(@sign, @significand, @exponent) + # end + # end + def self.num(x) - new(*x.split) + s, c, e = x.split + if x.zero? + e = :zero + else + case e + when :inf + e = :infinity + when :nan + e = :nan + end + end + new([s, c, e]) end # Endianness of the format (:little_endian, :big_endian or :little_big_endian) def self.endianness @endianness @@ -790,45 +817,10 @@ end end # from methods - - def self.nio_read_neutral(neutral) - if neutral.special? - case neutral.special - when :nan - return nan - when :inf - return infinity(neutral.sign=='-' ? -1 : +1) - end - end - if neutral.rep_pos<neutral.digits.length - nd = neutral.base==10 ? decimal_digits_necessary : (significand_digits*Math.log(radix)/Math.log(fmt.get_base)).ceil+1 - neutral = neutral.round(nd,:sig) - end - f = neutral.digits.to_i(neutral.base) - e = neutral.dec_pos-neutral.digits.length - case neutral.rounding - when :even - rounding = :half_even - when :inf - rounding = :half_up - when :zero - rounding = :half_down - when :truncate - rounding = :down - end - s = (neutral.sign=='-') ? -1 : +1 - if neutral.base!=radix - reader = Flt::Support::Reader.new(:mode=>:fixed) - s,f,e = reader.read(context, rounding, s, f, e, neutral.base).split - end - return_value s,f,e - - end - def self.from(*args) new(*args) end def self.from_bytes(b) @@ -837,21 +829,31 @@ def self.from_hex(hex) from_bytes Bytes.from_hex(hex) end - def self.from_number(v, mode=:approx) + def self.from_number(v, mode = :approx) if v.is_a?(Flt::Num) && v.num_class.radix==self.radix self.num(v) else - fmt = mode==:approx ? Nio::Fmt::CONV_FMT : Nio::Fmt::CONV_FMT_STRICT - nio_read(v.nio_write(fmt),fmt) + options = { + type: self, + exact_input: (mode != :approx), + output_mode: @normalized ? :fixed : :short + } + Numerals::Conversions.convert(v, options) end end - def self.from_text(txt, fmt=Nio::Fmt.default) # ? - nio_read(txt,fmt) + def self.from_text(txt, *args) + if @normalized + fmt = Numerals::Format[exact_input: true] + else + fmt = Numerals::Format[:short, exact_input: false] + end + fmt = fmt[*args] + fmt.read(txt, type: self) end def self.join(sign,significand,exponent) self.new sign,significand,exponent end @@ -873,12 +875,12 @@ end # Defines a floating-point number from a text representation of the # encoded integral value in a given base. # Returns a Value. - def self.from_bits_text(txt,base) - i = Integer.nio_read(txt,Nio::Fmt.base(base)) + def self.from_bits_text(txt, base) + i = Numerals::Format[].read(txt, type: Integer, base: base) from_bits i end # Converts en ancoded floating point number to hash containing # the internal fields that define the number. @@ -932,10 +934,52 @@ end pack_fields flds end end + class FltFmtConversion + + def initialize(form_class, options={}) + @form_class = form_class + @input_rounding = options[:input_rounding] + end + + def type + @form_class + end + + def order_of_magnitude(value, options={}) + num_conversions.order_of_maginitude(value.to_num, options) + end + + def number_of_digits(value, options={}) + num_conversions.number_of_digits(value.to_num, options) + end + + def exact?(value, options={}) + options[:exact] + end + + def write(number, exact_input, output_rounding) + # assert_equal @form_class, number.class + num_conversions.write(number.to_num, exact_input, output_rounding) + end + + def read(numeral, exact_input, approximate_simplified) + num = num_conversions.read(numeral, exact_input, approximate_simplified) + num = num.normalize(@form_class.context) if exact_input + @form_class.num(num) + end + + private + + def num_conversions + Numerals::Conversions[@form_class.context, input_rounding: @input_rounding] + end + + end + # :stopdoc: protected def self.define_fields(field_definitions) @field_lengths = [] @@ -1229,11 +1273,11 @@ e = encode_exponent(e, :integral_significand) end end s = sign_from_unit(s) m,e = neg_significand_exponent(0,m,e) if s%2==1 - pack_fields_hash :sign=>s, :significand=>m, :exponent=>e + pack_fields_hash sign: s, significand: m, exponent: e end # :startdoc: end # DPD (Densely-Packed-Decimal) formats @@ -1357,11 +1401,11 @@ i_combination = sig_msd|(exp_msb<<3) else i_combination = sig_msd|(1<<4)|(exp_msb<<1) end end - h = {:sign=>i_sign, :combination=>i_combination, :exponent_continuation=>i_exponent_continuation, :significand_continuation=>i_significand_continuation} + h = {sign: i_sign, combination: i_combination, exponent_continuation: i_exponent_continuation, significand_continuation: i_significand_continuation} fields = @internal_field_meaning.collect{|f| h[f]} Bytes.from_bitfields(@internal_field_lengths,fields,@endianness) end @@ -1425,11 +1469,11 @@ e = encode_exponent(e, :integral_significand) end end s = sign_from_unit(s) m,e = neg_significand_exponent(0,m,e) if s%2==1 - pack_fields_hash :sign=>s, :significand=>m, :exponent=>e, :type=>t + pack_fields_hash sign: s, significand: m, exponent: e, type: t end # :startdoc: @@ -1551,11 +1595,11 @@ e = encode_exponent(e, :integral_significand) end end s = sign_from_unit(s) m,e = neg_significand_exponent(0,m,e) if s%2==1 - pack_fields_hash :sign=>s, :significand=>m, :exponent=>e + pack_fields_hash sign: s, significand: m, exponent: e end # :startdoc: end @@ -1644,11 +1688,11 @@ e = encode_exponent(e, :integral_significand) end end s = sign_from_unit(s) m,e = neg_significand_exponent(0,m,e) if s%2==1 - pack_fields_hash :sign=>s, :significand=>m, :exponent=>e + pack_fields_hash sign: s, significand: m, exponent: e end def self.minus_sign_value (-1) % 2 @@ -1739,10 +1783,10 @@ # can actually have a variable precision. # For binary formats there's an option to gain one bit of precision # by adjusting the sign of the second number. This is enabled by the # :extra_prec option. # For example, the "double double" format used in PowerPC is this -# Flt.define :DoubleDouble, DoubleFormat, :half=>IEEE_binary64, :extra_prec=>true +# Flt.define :DoubleDouble, DoubleFormat, half: IEEE_binary64, extra_prec: true # Although this has a fixed 107 bits precision, the format as used in # the PowerPC can have greater precision for specific values (by having # greater separation between the exponents of both halves) class DoubleFormat < FormatBase