numerals 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,229 @@
1
+ # Rounding of Numerals
2
+ class Rounding
3
+
4
+ # Rounding is defined by the rounding mode and the precision,
5
+ # and is used to stablish the desired accuracy of a Numeral result.
6
+ #
7
+ # The rounding mode may be any of the valid Flt rounding modes
8
+ # (:half_even, :half_down, :half_up, :floor, :ceiling, :down, :up or :up05)
9
+ # or :exact for no rounding at all (to represent the situation where
10
+ # we want to get an exact numeral result.
11
+ #
12
+ # The precision may be defined either as a relative :precision value
13
+ # (number of significant digits of the result numeral) or as an :absolute
14
+ # :places that indicate the number of digits to the right of the fractional
15
+ # point. Negative values of :places will round to digits in integral positions.
16
+ #
17
+ # The base of the numerals to be rounded must also be defined (10 by default)
18
+ #
19
+ def initialize(*args)
20
+ if Hash === args.last
21
+ options = args.pop.dup
22
+ else
23
+ options = {}
24
+ end
25
+ args.each do |arg|
26
+ if Symbol === arg
27
+ options[:mode] = arg
28
+ elsif Integer === arg
29
+ options[:precision] = arg
30
+ else
31
+ raise "Invalid Rounding definition"
32
+ end
33
+ end
34
+ assign! options
35
+ end
36
+
37
+ attr_reader :mode, :base
38
+
39
+ include ModalSupport::BracketConstructor
40
+ include ModalSupport::StateEquivalent
41
+
42
+ def [](options={})
43
+ assign! options
44
+ end
45
+
46
+ def assign!(options)
47
+ @mode = options[:mode] || @mode
48
+ @precision = options[:precision] || @precision
49
+ @places = options[:places] || @places
50
+ @base = options[:base] || @base
51
+
52
+ if (@precision == 0 || @precision.nil?) && @places.nil?
53
+ @precision = 0
54
+ @mode = :exact
55
+ else
56
+ @mode ||= :half_even
57
+ end
58
+ if @mode == :exact
59
+ @precision = 0
60
+ @places = nil
61
+ end
62
+ @base ||= 10
63
+ self
64
+ end
65
+
66
+ def parameters
67
+ if exact?
68
+ { mode: :exact }
69
+ elsif relative?
70
+ { mode: @mode, precision: @precision }
71
+ elsif absolute?
72
+ { mode: @mode, places: @places }
73
+ end
74
+ end
75
+
76
+ def to_s
77
+ if exact?
78
+ "Rounding[:exact]"
79
+ elsif relative?
80
+ "Rounding[#{@mode.inspect}, precision: #{@precision}]"
81
+ elsif absolute?
82
+ "Rounding[#{@mode.inspect}, places: #{@places}]"
83
+ end
84
+ end
85
+
86
+ def inspect
87
+ to_s
88
+ end
89
+
90
+ def exact?
91
+ @mode == :exact
92
+ end
93
+
94
+ def absolute?
95
+ !exact? && !@precision
96
+ end
97
+
98
+ def relative?
99
+ !exact? && !absolute?
100
+ end
101
+
102
+ # Number of significant digits for a given numerical/numeral value
103
+ def precision(value)
104
+ if relative? || exact?
105
+ @precision
106
+ else
107
+ @places + num_integral_digits(value)
108
+ end
109
+ end
110
+
111
+ # Number of fractional placesfor a given numerical/numeral value
112
+ def places(value)
113
+ if absolute? || exact?
114
+ @places
115
+ else
116
+ @precision - num_integral_digits(value)
117
+ end
118
+ end
119
+
120
+ # Round a numeral. If the numeral has been truncated
121
+ # the :round_up option must be used to pass the information
122
+ # about the discarded digits:
123
+ # * nil if all discarded digits where 0 (the truncated value is exact)
124
+ # * :lo if there where non-zero discarded digits, but the first discarded digit
125
+ # is below half the base.
126
+ # * :tie if the first discarded was half the base and there where no more nonzero digits,
127
+ # i.e. the original value was a 'tie', exactly halfway between the truncated value
128
+ # and the next value with the same number of digits.
129
+ # * :hi if the original value was above the tie value.
130
+ def round(numeral, options={})
131
+ round_up = options[:round_up]
132
+ numeral, round_up = truncate(numeral, round_up)
133
+ adjust(numeral, round_up)
134
+ end
135
+
136
+ private
137
+
138
+ def check_base(numeral)
139
+ if numeral.base != @base
140
+ raise "Invalid Numeral (base #{numeral.base}) for a base #{@base} Rounding"
141
+ end
142
+ end
143
+
144
+ # Truncate a numeral and return also a round_up value with information about
145
+ # the digits beyond the truncation point that can be used to round the truncated
146
+ # numeral. If the numeral has already been truncated, the round_up result of
147
+ # that truncation should be passed as the second argument.
148
+ def truncate(numeral, round_up=nil)
149
+ check_base numeral
150
+ if exact?
151
+ round_up = nil
152
+ else
153
+ n = precision(numeral)
154
+ unless n==numeral.digits.size && numeral.approximate?
155
+ if n < numeral.digits.size - 1
156
+ rest_digits = numeral.digits[n+1..-1]
157
+ else
158
+ rest_digits = []
159
+ end
160
+ if numeral.repeating? && numeral.repeat < numeral.digits.size && n >= numeral.repeat
161
+ rest_digits += numeral.digits[numeral.repeat..-1]
162
+ end
163
+ digits = numeral.digits[0, n]
164
+ if digits.size < n
165
+ digits += (digits.size...n).map{|i| numeral.digit_value_at(i)}
166
+ end
167
+ if numeral.base % 2 == 0
168
+ tie_digit = numeral.base / 2
169
+ max_lo = tie_digit - 1
170
+ else
171
+ max_lo = numeral.base / 2
172
+ end
173
+ next_digit = numeral.digit_value_at(n)
174
+ if next_digit == 0
175
+ unless round_up.nil? && rest_digits.all?{|d| d == 0}
176
+ round_up = :lo
177
+ end
178
+ elsif next_digit <= max_lo # next_digit < tie_digit
179
+ round_up = :lo
180
+ elsif next_digit == tie_digit
181
+ if round_up || rest_digits.any?{|d| d != 0}
182
+ round_up = :hi
183
+ else
184
+ round_up = :tie
185
+ end
186
+ else # next_digit > tie_digit
187
+ round_up = :hi
188
+ end
189
+ numeral = Numeral[digits, point: numeral.point, sign: numeral.sign, normalize: :approximate]
190
+ end
191
+ end
192
+ [numeral, round_up]
193
+ end
194
+
195
+ # Adjust a truncated numeral using the round-up information
196
+ def adjust(numeral, round_up)
197
+ check_base numeral
198
+ point, digits = Flt::Support.adjust_digits(
199
+ numeral.point, numeral.digits.digits_array,
200
+ round_mode: @mode,
201
+ negative: numeral.sign == -1,
202
+ round_up: round_up,
203
+ base: numeral.base
204
+ )
205
+ Numeral[digits, point: point, base: numeral.base, sign: numeral.sign, normalize: :approximate]
206
+ end
207
+
208
+ ZERO_DIGITS = 0 # 1?
209
+
210
+ # Number of digits in the integer part of the value (excluding leading zeros).
211
+ def num_integral_digits(value)
212
+ case value
213
+ when 0
214
+ ZERO_DIGITS
215
+ when Numeral
216
+ if value.zero?
217
+ ZERO_DIGITS
218
+ else
219
+ if @base != value.base
220
+ value = value.to_base(@base)
221
+ end
222
+ value.normalized(remove_trailing_zeros: true).point
223
+ end
224
+ else
225
+ Conversions.order_of_magnitude(value, base: @base)
226
+ end
227
+ end
228
+
229
+ end
@@ -0,0 +1,10 @@
1
+ module Numerals
2
+
3
+ def self.gcd(a,b)
4
+ while b!=0 do
5
+ a,b = b, a.modulo(b)
6
+ end
7
+ return a.abs
8
+ end
9
+
10
+ end
@@ -0,0 +1,3 @@
1
+ module Numerals
2
+ VERSION = "0.0.0"
3
+ end
data/lib/numerals.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'modalsupport'
2
+
3
+ require "numerals/version"
4
+ require 'numerals/numeral'
5
+ require 'numerals/rounding'
6
+ require 'numerals/conversions/float'
7
+ require 'numerals/conversions/integer'
8
+ require 'numerals/conversions/rational'
9
+ require 'numerals/conversions/bigdecimal'
10
+ require 'numerals/conversions/flt'
11
+ require 'numerals/formatting/digits_definition'
12
+ require 'numerals/formatting/options'
data/numerals.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'numerals/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "numerals"
8
+ spec.version = Numerals::VERSION
9
+ spec.authors = ["Javier Goizueta"]
10
+ spec.email = ["jgoizueta@gmail.com"]
11
+ spec.summary = %q{Number representation as text.}
12
+ spec.description = %q{Number formatting and reading.}
13
+ spec.homepage = "https://github.com/jgoizueta/numerals"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'flt', "~> 1.3.4"
22
+ spec.add_dependency 'modalsupport', "~> 0.9.2"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.6"
25
+ spec.add_development_dependency "rake"
26
+ end
data/test/data.yaml ADDED
@@ -0,0 +1,101 @@
1
+ ---
2
+ - A91C2BB757F72141
3
+ - D84014519CD8253F
4
+ - BF30A46FFF51ED3F
5
+ - 91D03B54623CC03E
6
+ - D403F243699B4B3F
7
+ - 6D1F99449A26D540
8
+ - 92817CDBC7715E3F
9
+ - 72696E8F13BB323F
10
+ - 683C16C10F79FE3E
11
+ - 22801BA12A074740
12
+ - 7081D3172A53E5BF
13
+ - C0DE790FC0FBE9BF
14
+ - 3AE5F0923E55D33F
15
+ - 92B6BEEC140A8FBF
16
+ - CE66ABACDBD80F3F
17
+ - ACC70B95DCFF6BC0
18
+ - 1461EF81707378BF
19
+ - AE09EC08D0F1ADBF
20
+ - 34DA3FFF72E9D83F
21
+ - C664AD3F9AC4BFBF
22
+ - 2101BE78DFDC74BE
23
+ - 7EE9B042D4628140
24
+ - 90368189CC8D753F
25
+ - 337C9D874DC4C5BE
26
+ - 0CFDA79A7FDCED3F
27
+ - EC94C739F2CFB3BF
28
+ - 1D5436848A1AEBBE
29
+ - B725B6C420034640
30
+ - 48C692FEAA59DEBF
31
+ - BC4CB816B0BAB5BF
32
+ - C2D835BC80B99A40
33
+ - 6A565C2E720847BF
34
+ - 050189AF84C84EC1
35
+ - 0060B28B63B5DC3F
36
+ - 18A387F2A64E5FC0
37
+ - FBC8B0D428E302C1
38
+ - A30DD43ED762B4C0
39
+ - 3D85522EC70AFDBE
40
+ - FCF62D2923F0583F
41
+ - DB588C2A9C26FBC0
42
+ - BA4E1863DBE6FCBE
43
+ - 5C34946E1A12D33F
44
+ - 90E63D7E81888740
45
+ - 82541C0DE9F1443F
46
+ - 9F2A7781EE4B683F
47
+ - 0082C4B6393EA7BF
48
+ - 768EDD6B97D19540
49
+ - 87BA76258BEBFBBE
50
+ - 7A6580A6639DD7BF
51
+ - 26DDD435F13B12BF
52
+ - 1AF857C69682DBBF
53
+ - E731C0DCDAAE51BF
54
+ - DB9DB0551A511F3F
55
+ - 04977B9C4C73D13F
56
+ - 44D5BB96C84587C0
57
+ - CE3DE8C2DF9942BF
58
+ - 23F12DE055F30C3F
59
+ - C10951998E1B7440
60
+ - 1CC3F51802F309C0
61
+ - FB97E7DB6B081DC1
62
+ - 5C811354280FEA3F
63
+ - A405ED0A749718BF
64
+ - 322939F71F7A51BF
65
+ - F3E06C6EEB2342BF
66
+ - 5A6D9DF3F879D3C0
67
+ - BC466E35C5AF9940
68
+ - EB4571E54EB7CD3F
69
+ - BBA9A8401CD4D13F
70
+ - 6A565DEA5E4F8A40
71
+ - 20586794310B93BF
72
+ - 80B8449C203B9BC0
73
+ - 0E9C461358F4AABF
74
+ - 469E0A1D42DEA73F
75
+ - 711DA636DD2C41BF
76
+ - 414A9E166BE2A0C0
77
+ - 407FF9F39253F2BE
78
+ - BAD158256942D4C0
79
+ - 2D4424FE12BE94BF
80
+ - 7F0F15B537E997C0
81
+ - 4ADF1BD93C66F1BF
82
+ - 2DAF002ADBC1E0BF
83
+ - 3739FF3C2BE601C0
84
+ - 38ACCEAE7B88083F
85
+ - 7C2D9D7FA150C23F
86
+ - B726087920E8EA3F
87
+ - C075B44F459250BE
88
+ - 9EDAB9C61F7D783F
89
+ - 2FE6362CACE5153F
90
+ - 98A24B93A2F4C33E
91
+ - 6FD39CEB5C7F15C0
92
+ - C5BE550840E4503F
93
+ - 214EA7E4F9619640
94
+ - 4DEF9253DE1C10C0
95
+ - B8C76C5B85E4FDBF
96
+ - 07D21EB7A400F13F
97
+ - DB103669202EAEBF
98
+ - 9F59E7B3EA71E63F
99
+ - 83712959474BF440
100
+ - 8D5B7FA245A650BF
101
+ - 16FAF25CCBB8ECBE
data/test/helper.rb ADDED
@@ -0,0 +1,40 @@
1
+ require 'test/unit'
2
+ # require 'minitest/spec'
3
+ # require 'minitest/unit'
4
+ $: << "." unless $:.include?(".") # for Ruby 1.9.2
5
+ require File.expand_path(File.join(File.dirname(__FILE__),'/../lib/numerals'))
6
+
7
+ require 'yaml'
8
+
9
+ module PrepareData
10
+
11
+ @@data = []
12
+
13
+ def self.add(x)
14
+ @@data << [x].pack('E').unpack('H*')[0].upcase
15
+ end
16
+
17
+ def self.init
18
+ unless File.exist?('test/data.yaml')
19
+ 100.times do
20
+ x = rand
21
+ x *= rand(1000) if rand<0.5
22
+ x /= rand(1000) if rand<0.5
23
+ x *= rand(9999) if rand<0.5
24
+ x /= rand(9999) if rand<0.5
25
+ x = -x if rand<0.5
26
+ #puts x
27
+ add x
28
+ end
29
+ add 1.0/3
30
+ add 0.1
31
+ File.open('test/data.yaml','w') { |out| out << @@data.to_yaml }
32
+ end
33
+ end
34
+ end
35
+
36
+ PrepareData.init
37
+
38
+ def BigDec(x)
39
+ BigDecimal.new(x.to_s)
40
+ end
@@ -0,0 +1,110 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path(File.join(File.dirname(__FILE__),'helper.rb'))
4
+ require 'test/unit'
5
+ include Numerals
6
+ require 'yaml'
7
+
8
+ class TestDigitsDefinition < Test::Unit::TestCase
9
+
10
+ DEFAULT_DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
11
+ MAX_TEST_BASE = DEFAULT_DIGITS.size
12
+
13
+ def default_digits(b)
14
+ DEFAULT_DIGITS[0,b]
15
+ end
16
+
17
+ def define_from_digits(b)
18
+ DigitsDefinition[default_digits(b)]
19
+ end
20
+
21
+ def define_from_base(b)
22
+ DigitsDefinition[base: b]
23
+ end
24
+
25
+ def check_base(b, digits)
26
+ assert_equal b, digits.radix
27
+ (0...b).each do |digit_value|
28
+ digit_char = default_digits(b)[digit_value]
29
+ assert_equal digit_char, digits.digit_char(digit_value)
30
+ assert_equal digit_value, digits.digit_value(digit_char)
31
+ assert digits.is_digit?(digit_char)
32
+ end
33
+ [-10,-5,-2,-1,b,b+1,b+2,b+10].each do |invalid_digit_value|
34
+ assert_nil digits.digit_char(invalid_digit_value)
35
+ end
36
+ invalid_chars = %w(- / & ñ)
37
+ if b < MAX_TEST_BASE
38
+ (b+1...MAX_TEST_BASE).each do |i|
39
+ invalid_chars << DEFAULT_DIGITS[i]
40
+ end
41
+ end
42
+ invalid_chars.each do |invalid_digit_char|
43
+ assert_nil digits.digit_value(invalid_digit_char)
44
+ assert !digits.is_digit?(invalid_digit_char)
45
+ end
46
+ end
47
+
48
+ def test_digits_definition
49
+ (0..MAX_TEST_BASE).each do |base|
50
+ check_base base, define_from_digits(base)
51
+ check_base base, define_from_base(base)
52
+ end
53
+ end
54
+
55
+ def test_digits_case
56
+ uppercase_digits = DigitsDefinition[base: 16, downcase: false]
57
+
58
+ assert_equal 10, uppercase_digits.digit_value('A')
59
+ assert_equal 11, uppercase_digits.digit_value('B')
60
+ assert_equal 15, uppercase_digits.digit_value('F')
61
+
62
+ assert_equal 10, uppercase_digits.digit_value('a')
63
+ assert_equal 11, uppercase_digits.digit_value('b')
64
+ assert_equal 15, uppercase_digits.digit_value('f')
65
+
66
+ assert_equal 'A', uppercase_digits.digit_char(10)
67
+ assert_equal 'B', uppercase_digits.digit_char(11)
68
+ assert_equal 'F', uppercase_digits.digit_char(15)
69
+
70
+ downcase_digits = DigitsDefinition[base: 16, downcase: true]
71
+
72
+ assert_equal 10, downcase_digits.digit_value('A')
73
+ assert_equal 11, downcase_digits.digit_value('B')
74
+ assert_equal 15, downcase_digits.digit_value('F')
75
+
76
+ assert_equal 10, downcase_digits.digit_value('a')
77
+ assert_equal 11, downcase_digits.digit_value('b')
78
+ assert_equal 15, downcase_digits.digit_value('f')
79
+
80
+ assert_equal 'a', downcase_digits.digit_char(10)
81
+ assert_equal 'b', downcase_digits.digit_char(11)
82
+ assert_equal 'f', downcase_digits.digit_char(15)
83
+
84
+
85
+ cs_uppercase_digits = DigitsDefinition[base: 16, downcase: false, case_sensitive: true]
86
+
87
+ assert_equal 10, cs_uppercase_digits.digit_value('A')
88
+ assert_equal 11, cs_uppercase_digits.digit_value('B')
89
+ assert_equal 15, cs_uppercase_digits.digit_value('F')
90
+ assert_nil cs_uppercase_digits.digit_value('a')
91
+ assert_nil cs_uppercase_digits.digit_value('b')
92
+ assert_nil cs_uppercase_digits.digit_value('f')
93
+ assert_equal 'A', cs_uppercase_digits.digit_char(10)
94
+ assert_equal 'B', cs_uppercase_digits.digit_char(11)
95
+ assert_equal 'F', cs_uppercase_digits.digit_char(15)
96
+
97
+ cs_downcase_digits = DigitsDefinition[base: 16, downcase: true, case_sensitive: true]
98
+
99
+ assert_equal 10, cs_downcase_digits.digit_value('a')
100
+ assert_equal 11, cs_downcase_digits.digit_value('b')
101
+ assert_equal 15, cs_downcase_digits.digit_value('f')
102
+ assert_nil cs_downcase_digits.digit_value('A')
103
+ assert_nil cs_downcase_digits.digit_value('B')
104
+ assert_nil cs_downcase_digits.digit_value('F')
105
+ assert_equal 'a', cs_downcase_digits.digit_char(10)
106
+ assert_equal 'b', cs_downcase_digits.digit_char(11)
107
+ assert_equal 'f', cs_downcase_digits.digit_char(15)
108
+ end
109
+
110
+ end
@@ -0,0 +1,58 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__),'helper.rb'))
2
+
3
+ require 'numerals'
4
+ include Numerals
5
+
6
+ class TestFloatConversions < Test::Unit::TestCase # < Minitest::Test
7
+
8
+ def test_special
9
+ assert_equal Numeral.nan, Conversions.number_to_numeral(Float.context.nan)
10
+ assert_equal Numeral.nan, Conversions.number_to_numeral(Float.context.nan, :fixed, Rounding[:exact, base: 2])
11
+ assert_equal Numeral.nan, Conversions.number_to_numeral(Float.context.nan, :fixed, Rounding[:exact, base: 10])
12
+ assert_equal Numeral.nan, Conversions.number_to_numeral(Float.context.nan, :fixed, Rounding[precision: 10, base: 10])
13
+ assert_equal Numeral.nan, Conversions.number_to_numeral(Float.context.nan, :free)
14
+
15
+ assert_equal Numeral.infinity, Conversions.number_to_numeral(Float.context.infinity)
16
+ assert_equal Numeral.infinity, Conversions.number_to_numeral(Float.context.infinity, :fixed, Rounding[:exact, base: 2])
17
+ assert_equal Numeral.infinity, Conversions.number_to_numeral(Float.context.infinity, :fixed, Rounding[:exact, base: 10])
18
+ assert_equal Numeral.infinity, Conversions.number_to_numeral(Float.context.infinity, :fixed, Rounding[precision: 10, base: 10])
19
+ assert_equal Numeral.infinity, Conversions.number_to_numeral(Float.context.infinity, :free)
20
+
21
+ assert_equal Numeral.infinity(-1), Conversions.number_to_numeral(Float.context.infinity(-1))
22
+ assert_equal Numeral.infinity(-1), Conversions.number_to_numeral(Float.context.infinity(-1), :fixed, Rounding[:exact, base: 2])
23
+ assert_equal Numeral.infinity(-1), Conversions.number_to_numeral(Float.context.infinity(-1), :fixed, Rounding[:exact, base: 10])
24
+ assert_equal Numeral.infinity(-1), Conversions.number_to_numeral(Float.context.infinity(-1), :fixed, Rounding[precision: 10, base: 10])
25
+ assert_equal Numeral.infinity(-1), Conversions.number_to_numeral(Float.context.infinity(-1), :free)
26
+
27
+ assert Conversions.numeral_to_number(Numeral.nan, Float).nan?
28
+ assert_equal Float.context.infinity, Conversions.numeral_to_number(Numeral.infinity, Float)
29
+ assert_equal Float.context.infinity(-1), Conversions.numeral_to_number(Numeral.infinity(-1), Float)
30
+ end
31
+
32
+ def test_exact
33
+ assert_equal Numeral[1,point:1], Conversions.number_to_numeral(1.0, :fixed, Rounding[:exact, base: 10])
34
+ assert_equal Numeral[1,point:1, sign: -1], Conversions.number_to_numeral(-1.0, :fixed, Rounding[:exact, base: 10])
35
+
36
+ assert_equal Numeral[1,point:1, base: 2], Conversions.number_to_numeral(1.0, :fixed, Rounding[:exact, base: 2])
37
+ assert_equal Numeral[1,point:1, sign: -1, base: 2], Conversions.number_to_numeral(-1.0, :fixed, Rounding[:exact, base: 2])
38
+
39
+ [0.1, 0.01, 0.001, 1/3.0, 10/3.0, 100/3.0, Math::PI, 0.5, 123.0, 123.45, 1.23E32, 1.23E-32].each do |x|
40
+ [x, -x].each do |y|
41
+ numeral = exact_decimal(y)
42
+ tmp = Conversions.number_to_numeral(y, :fixed, Rounding[:exact, base: 10])
43
+ assert_equal numeral, Conversions.number_to_numeral(y, :fixed, Rounding[:exact, base: 10]), "#{y} to base 10 exact numeral"
44
+ assert_equal y, Conversions.numeral_to_number(numeral, Float), "#{x} base 10 numeral to float"
45
+ end
46
+ end
47
+ end
48
+
49
+ def exact_decimal(x)
50
+ Flt::DecNum.context(exact: true){
51
+ Flt::BinNum.context(Flt::BinNum::FloatContext){
52
+ d = Flt::BinNum(x).to_decimal_exact
53
+ Numeral[d.coefficient.to_s.chars.map(&:to_i), sign: d.sign, point: d.fractional_exponent, normalize: :exact]
54
+ }
55
+ }
56
+ end
57
+
58
+ end