Skip to content

Commit

Permalink
Context option to normalize all results
Browse files Browse the repository at this point in the history
This allows for easy replication of fixed precision floating point
  • Loading branch information
jgoizueta committed Apr 6, 2015
1 parent d8651d5 commit 9d648da
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 11 deletions.
50 changes: 39 additions & 11 deletions lib/flt/num.rb
Expand Up @@ -422,6 +422,7 @@ class ContextBase
# taken as emin and emax=1-emin. Such limits comply with IEEE 754-2008
# * :capitals : (true or false) to use capitals in text representations
# * :clamp : (true or false) enables clamping
# * :normalized : (true or false) normalizes all results
#
# See also the context constructor method Flt::Num.Context().
def initialize(num_class, *options)
Expand All @@ -441,6 +442,7 @@ def initialize(num_class, *options)
@coercible_type_handlers = num_class.base_coercible_types.dup
@conversions = num_class.base_conversions.dup
@angle = :rad # angular units: :rad (radians) / :deg (degrees) / :grad (gradians)
@normalized = false
end
assign options.first

Expand Down Expand Up @@ -515,7 +517,11 @@ def int_div_radix_power(x,n)
@num_class.int_div_radix_power(x,n)
end

attr_accessor :rounding, :emin, :emax, :flags, :traps, :ignored_flags, :capitals, :clamp, :angle
attr_accessor :rounding, :emin, :emax, :flags, :traps, :ignored_flags, :capitals, :clamp, :angle, :normalized

def normalized?
normalized
end

# TODO: consider the convenience of adding accessors of this kind:
# def rounding(new_rounding=nil)
Expand Down Expand Up @@ -630,9 +636,10 @@ def assign(options)
@emin = options[:emin] unless options[:emin].nil?
@emax = options[:emax] unless options[:emax].nil?
@capitals = options[:capitals ] unless options[:capitals ].nil?
@clamp = options[:clamp ] unless options[:clamp ].nil?
@exact = options[:exact ] unless options[:exact ].nil?
@angle = options[:angle ] unless options[:angle ].nil?
@clamp = options[:clamp] unless options[:clamp].nil?
@exact = options[:exact] unless options[:exact].nil?
@angle = options[:angle] unless options[:angle].nil?
@normalized = options[:normalized] unless options[:normalized].nil?
update_precision
if options[:extra_precision] && !@exact
@precision += options[:extra_precision]
Expand Down Expand Up @@ -660,6 +667,7 @@ def copy_from(other)
@coercible_type_handlers = other.coercible_type_handlers.dup
@conversions = other.conversions.dup
@angle = other.angle
@normalized = other.normalized
end

def dup
Expand Down Expand Up @@ -1477,18 +1485,19 @@ def math(*args, &blk)
# * :base is the numeric base of the input, 10 by default.
def initialize(*args)
options = args.pop if args.last.is_a?(Hash)
context = args.pop if args.size>0 && (args.last.kind_of?(ContextBase) || args.last.nil?)
context ||= options && options.delete(:context)
options ||= {}
context = args.pop if args.size > 0 && (args.last.kind_of?(ContextBase) || args.last.nil?)
context ||= options.delete(:context)
mode = args.pop if args.last.is_a?(Symbol) && ![:inf, :nan, :snan].include?(args.last)
args = args.first if args.size==1 && args.first.is_a?(Array)
if args.empty? && options
if args.empty? && !options.empty?
args = [options.delete(:sign)||+1,
options.delete(:coefficient) || 0,
options.delete(:exponent) || 0]
end
mode ||= options && options.delete(:mode)
base = (options && options.delete(:base))
context = options if context.nil? && options && !options.empty?
mode ||= options.delete(:mode)
base = options.delete(:base)
context = options if context.nil? && !options.empty?
context = define_context(context)

case args.size
Expand Down Expand Up @@ -3593,14 +3602,33 @@ def _fix(context)
return ans
end
if context.normalized?
exp = @exp
coeff = @coeff
if self_is_subnormal
if exp > context.etiny
coeff = num_class.int_mult_radix_power(coeff, exp - context.etiny)
exp = context.etiny
end
else
min_normal_coeff = context.minimum_normalized_coefficient
while coeff < min_normal_coeff
coeff = num_class.int_mult_radix_power(coeff, 1)
exp -= 1
end
end
if exp != @exp || coeff != @coeff
return Num(@sign, coeff, exp)
end
end
if context.clamp? && @exp>etop
context.exception Clamped
self_padded = num_class.int_mult_radix_power(@coeff, @exp-etop)
return Num(sign,self_padded,etop)
end
return Num(self)
end
# adjust payload of a NaN to the context
Expand Down
67 changes: 67 additions & 0 deletions test/test_normalized.rb
@@ -0,0 +1,67 @@
require File.expand_path(File.join(File.dirname(__FILE__),'helper.rb'))


class TestNormalized < Test::Unit::TestCase

include Flt

def setup
initialize_context
end

def test_giac_floating_point
BinNum.context.precision = 48
BinNum.context.rounding = :down
BinNum.context.normalized = true
x = (BinNum(4)/3-1)*3 - 1
assert_equal [-1, 140737488355328, -93], x.split

x = BinNum(11)/15
assert_equal [1, 206414982921147, -48], x.split
x *= BinNum('1E308')
assert_equal [1, 229644291251027, 975], x.split
end

def test_normalized_context
refute DecNum.context.normalized?
DecNum.context(normalized: true) do
assert DecNum.context.normalized?
assert_equal [1, 1, -1], DecNum('0.1').split
assert_equal [1, 100000000, -9], (+DecNum('0.1')).split
assert_equal [1, 100000000, -9], (DecNum('0.1')+0).split
assert_equal [1, 100000000, -9], (DecNum('0.1')/1).split
end
refute DecNum.context.normalized?
assert_equal [1, 1, -1], DecNum('0.1').split
assert_equal [1, 1, -1], (+DecNum('0.1')).split
assert_equal [1, 1, -1], (DecNum('0.1')+0).split
assert_equal [1, 1, -1], (DecNum('0.1')/1).split
context = DecNum.context(normalized: true)
assert context.normalized?
assert_equal [1, 100000000, -9], context.plus(DecNum('0.1')).split
assert_equal [1, 100000000, -9], context.add(DecNum('0.1'), 0).split
assert_equal [1, 100000000, -9], context.divide(DecNum('0.1'), 1).split
refute DecNum.context.normalized?
assert_equal [1, 1, -1], DecNum('0.1').split
assert_equal [1, 1, -1], (+DecNum('0.1')).split
assert_equal [1, 1, -1], (DecNum('0.1')+0).split
assert_equal [1, 1, -1], (DecNum('0.1')/1).split
end

def test_precision
x = (DecNum(4)/3 - 1)*3 - 1 # -1e-8
assert_equal [-1, 1, -8], x.split

DecNum.context.normalized = true
x = (DecNum(4)/3 - 1)*3 - 1 # -1.00000000e-8
assert_equal [-1, 100000000, -16], x.split

x = (BinNum(4)/3 - 1)*3 - 1 # -0x1p-52
assert_equal [-1, 1, -52], x.split

BinNum.context.normalized = true
x = (BinNum(4)/3 - 1)*3 - 1 # -0x1.0000000000000p-52
assert_equal [-1, 4503599627370496, -104], x.split
end

end

0 comments on commit 9d648da

Please sign in to comment.