numerals 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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