numerals 0.0.0 → 0.1.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.
- checksums.yaml +4 -4
- data/README.md +149 -5
- data/lib/numerals/conversions/bigdecimal.rb +209 -9
- data/lib/numerals/conversions/context_conversion.rb +40 -0
- data/lib/numerals/conversions/float.rb +106 -71
- data/lib/numerals/conversions/flt.rb +115 -44
- data/lib/numerals/conversions/integer.rb +32 -3
- data/lib/numerals/conversions/rational.rb +27 -3
- data/lib/numerals/conversions.rb +74 -33
- data/lib/numerals/digits.rb +8 -5
- data/lib/numerals/format/base_scaler.rb +160 -0
- data/lib/numerals/format/exp_setter.rb +218 -0
- data/lib/numerals/format/format.rb +257 -0
- data/lib/numerals/format/input.rb +140 -0
- data/lib/numerals/format/mode.rb +157 -0
- data/lib/numerals/format/notation.rb +51 -0
- data/lib/numerals/format/notations/html.rb +53 -0
- data/lib/numerals/format/notations/latex.rb +48 -0
- data/lib/numerals/format/notations/text.rb +141 -0
- data/lib/numerals/format/output.rb +167 -0
- data/lib/numerals/format/symbols.rb +565 -0
- data/lib/numerals/format/text_parts.rb +35 -0
- data/lib/numerals/format.rb +25 -0
- data/lib/numerals/formatting_aspect.rb +36 -0
- data/lib/numerals/numeral.rb +34 -21
- data/lib/numerals/repeat_detector.rb +99 -0
- data/lib/numerals/rounding.rb +340 -181
- data/lib/numerals/version.rb +1 -1
- data/lib/numerals.rb +4 -2
- data/numerals.gemspec +1 -1
- data/test/test_base_scaler.rb +189 -0
- data/test/test_big_conversions.rb +105 -0
- data/test/test_digits_definition.rb +23 -28
- data/test/test_exp_setter.rb +732 -0
- data/test/test_float_conversions.rb +48 -30
- data/test/test_flt_conversions.rb +476 -80
- data/test/test_format.rb +124 -0
- data/test/test_format_input.rb +226 -0
- data/test/test_format_mode.rb +124 -0
- data/test/test_format_output.rb +789 -0
- data/test/test_integer_conversions.rb +22 -22
- data/test/test_numeral.rb +35 -0
- data/test/test_rational_conversions.rb +28 -28
- data/test/test_repeat_detector.rb +72 -0
- data/test/test_rounding.rb +158 -0
- data/test/test_symbols.rb +32 -0
- metadata +38 -5
- data/lib/numerals/formatting/digits_definition.rb +0 -75
@@ -1,20 +1,16 @@
|
|
1
|
-
require 'numerals/conversions'
|
2
|
-
require 'flt/float'
|
1
|
+
require 'numerals/conversions/context_conversion'
|
3
2
|
|
4
|
-
class Numerals::FloatConversion
|
3
|
+
class Numerals::FloatConversion < Numerals::ContextConversion
|
5
4
|
|
5
|
+
# Options:
|
6
|
+
#
|
7
|
+
# * :input_rounding (optional, a non-exact Rounding or rounding mode)
|
8
|
+
# which is used when input is approximate as the assumed rounding
|
9
|
+
# mode which would be used so that the result numeral rounds back
|
10
|
+
# to the input number
|
11
|
+
#
|
6
12
|
def initialize(options={})
|
7
|
-
|
8
|
-
@context = @type.context
|
9
|
-
options = { use_native_float: true }.merge(options)
|
10
|
-
@use_native_float = options[:use_native_float]
|
11
|
-
# @rounding_mode if used for :free numeral to number conversion
|
12
|
-
# and should be the implied rounding mode of the invers conversion
|
13
|
-
# (number to numeral);
|
14
|
-
# TODO: it should be possible to assign it for higher level
|
15
|
-
# formatting handling.
|
16
|
-
@rounding_mode = @context.rounding
|
17
|
-
@honor_rounding = true
|
13
|
+
super Float, options
|
18
14
|
end
|
19
15
|
|
20
16
|
def order_of_magnitude(value, options={})
|
@@ -26,6 +22,19 @@ class Numerals::FloatConversion
|
|
26
22
|
end
|
27
23
|
end
|
28
24
|
|
25
|
+
def number_of_digits(value, options={})
|
26
|
+
base = options[:base] || 10
|
27
|
+
if base == @context.radix
|
28
|
+
@context.precision
|
29
|
+
else
|
30
|
+
@context.necessary_digits(base)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def exact?(value, options={})
|
35
|
+
options[:exact]
|
36
|
+
end
|
37
|
+
|
29
38
|
# mode is either :exact or :approximate
|
30
39
|
def number_to_numeral(number, mode, rounding)
|
31
40
|
if @context.special?(number)
|
@@ -49,70 +58,87 @@ class Numerals::FloatConversion
|
|
49
58
|
end
|
50
59
|
end
|
51
60
|
|
61
|
+
def write(number, exact_input, output_rounding)
|
62
|
+
output_base = output_rounding.base
|
63
|
+
input_base = @context.radix
|
64
|
+
|
65
|
+
if @context.special?(number)
|
66
|
+
special_float_to_numeral number
|
67
|
+
elsif exact_input
|
68
|
+
if output_base == input_base && output_rounding.free?
|
69
|
+
# akin to number.format(base: output_base, simplified: true)
|
70
|
+
general_float_to_numeral number, output_rounding, false
|
71
|
+
else
|
72
|
+
# akin to number.format(base: output_base, exact: true)
|
73
|
+
exact_float_to_numeral number, output_rounding
|
74
|
+
end
|
75
|
+
else
|
76
|
+
if output_base == input_base && output_rounding.preserving?
|
77
|
+
# akin to number.format(base: output_base)
|
78
|
+
sign, coefficient, exp = @context.split(number)
|
79
|
+
Numerals::Numeral.from_coefficient_scale(
|
80
|
+
sign*coefficient, exp,
|
81
|
+
approximate: true, base: output_base
|
82
|
+
)
|
83
|
+
elsif output_rounding.simplifying?
|
84
|
+
# akin to number.forma(base: output_base, simplify: true)
|
85
|
+
general_float_to_numeral number, output_rounding, false
|
86
|
+
else
|
87
|
+
# akin to number.forma(base: output_base, all_digits: true)
|
88
|
+
general_float_to_numeral number, output_rounding, true
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def read(numeral, exact_input, approximate_simplified)
|
94
|
+
if numeral.special?
|
95
|
+
special_numeral_to_float numeral
|
96
|
+
# elsif numeral.approximate? && !exact_input
|
97
|
+
# if approximate_simplified
|
98
|
+
# # akin to @context.Num(numeral_text, :short)
|
99
|
+
# short_numeral_to_float numeral
|
100
|
+
# else
|
101
|
+
# # akin to @context.Num(numeral_text, :free)
|
102
|
+
# free_numeral_to_float numeral
|
103
|
+
# end
|
104
|
+
else
|
105
|
+
# akin to @context.Num(numeral_text, :fixed)
|
106
|
+
fixed_numeral_to_float numeral
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
52
110
|
private
|
53
111
|
|
54
112
|
def special_float_to_numeral(x)
|
55
113
|
if x.nan?
|
56
|
-
Numeral.nan
|
114
|
+
Numerals::Numeral.nan
|
57
115
|
elsif x.infinite?
|
58
|
-
Numeral.infinity @context.sign(x)
|
116
|
+
Numerals::Numeral.infinity @context.sign(x)
|
59
117
|
end
|
60
118
|
end
|
61
119
|
|
62
120
|
def exact_float_to_numeral(number, rounding)
|
63
121
|
quotient = number.to_r
|
64
|
-
numeral = Numeral.from_quotient(quotient, base: rounding.base)
|
65
|
-
unless rounding.
|
122
|
+
numeral = Numerals::Numeral.from_quotient(quotient, base: rounding.base)
|
123
|
+
unless rounding.free?
|
66
124
|
numeral = rounding.round(numeral)
|
67
125
|
end
|
68
126
|
numeral
|
69
127
|
end
|
70
128
|
|
71
129
|
def approximate_float_to_numeral(number, rounding)
|
72
|
-
all_digits = !rounding.
|
130
|
+
all_digits = !rounding.free?
|
73
131
|
general_float_to_numeral(number, rounding, all_digits)
|
74
132
|
end
|
75
133
|
|
76
|
-
# def fixed_float_to_numeral(number, rounding)
|
77
|
-
# # adjust to rounding.precision
|
78
|
-
# if rounding.exact?
|
79
|
-
# # if simplify
|
80
|
-
# # number = @context.rationalize(simplify)
|
81
|
-
# # end
|
82
|
-
# exact_float_to_numeral number, rounding.base
|
83
|
-
# else
|
84
|
-
# if rounding.base == 10 && @use_native_float
|
85
|
-
# native_float_to_numeral number, rounding
|
86
|
-
# else
|
87
|
-
# general_float_to_numeral number, rounding, true
|
88
|
-
# end
|
89
|
-
# end
|
90
|
-
# end
|
91
|
-
|
92
|
-
# def free_float_to_numeral(number, rounding)
|
93
|
-
# # free mode ignores output precision (rounding) and
|
94
|
-
# # produces the result based only on the number precision
|
95
|
-
# rounding = Rounding[:exact, base: rounding.base]
|
96
|
-
# general_float_to_numeral number, rounding, false
|
97
|
-
# end
|
98
|
-
|
99
|
-
def native_float_to_numeral(number, rounding)
|
100
|
-
need_to_round = (rounding.mode != @context.rounding)
|
101
|
-
n = need_to_round ? Float::DECIMAL_DIG : rounding.precision
|
102
|
-
txt = format("%.*e", n - 1, number)
|
103
|
-
numeral = text_to_numeral(txt, normalize: :approximate) # C-Locale text to numeral...
|
104
|
-
numeral = rounding.round(numeral) if need_to_round
|
105
|
-
numeral
|
106
|
-
end
|
107
|
-
|
108
134
|
def general_float_to_numeral(x, rounding, all_digits)
|
109
|
-
sign, coefficient, exponent =
|
110
|
-
precision =
|
135
|
+
sign, coefficient, exponent = @context.split(x)
|
136
|
+
precision = @context.precision
|
111
137
|
output_base = rounding.base
|
112
138
|
|
113
|
-
# here rounding_mode
|
114
|
-
|
115
|
-
|
139
|
+
# here rounding_mode is not the output rounding mode, but the rounding mode used for input
|
140
|
+
rounding_mode = (@input_rounding || rounding).mode
|
141
|
+
|
116
142
|
formatter = Flt::Support::Formatter.new(
|
117
143
|
@context.radix, @context.etiny, output_base, raise_on_repeat: false
|
118
144
|
)
|
@@ -122,10 +148,14 @@ class Numerals::FloatConversion
|
|
122
148
|
|
123
149
|
dec_pos, digits = formatter.digits
|
124
150
|
rep_pos = formatter.repeat
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
151
|
+
|
152
|
+
normalization = :approximate
|
153
|
+
|
154
|
+
numeral = Numerals::Numeral[digits, sign: sign, point: dec_pos, rep_pos: rep_pos, base: output_base,
|
155
|
+
normalize: normalization]
|
156
|
+
|
157
|
+
numeral = rounding.round(numeral, round_up: formatter.round_up)
|
158
|
+
|
129
159
|
numeral
|
130
160
|
end
|
131
161
|
|
@@ -139,8 +169,7 @@ class Numerals::FloatConversion
|
|
139
169
|
end
|
140
170
|
|
141
171
|
def fixed_numeral_to_float(numeral)
|
142
|
-
|
143
|
-
# return exact_numeral_to_float(numeral) if numeral.exact?
|
172
|
+
return exact_numeral_to_float(numeral) if numeral.exact?
|
144
173
|
if numeral.base == @context.radix
|
145
174
|
same_base_numeral_to_float numeral
|
146
175
|
else
|
@@ -148,9 +177,9 @@ class Numerals::FloatConversion
|
|
148
177
|
# to a numeral preserving its value.
|
149
178
|
representable_digits = @context.representable_digits(numeral.base)
|
150
179
|
k = numeral.scale
|
151
|
-
if !@
|
180
|
+
if !@input_rounding && numeral.digits.size <= representable_digits && k.abs <= representable_digits
|
152
181
|
representable_numeral_to_float numeral
|
153
|
-
elsif !@
|
182
|
+
elsif !@input_rounding && (k > 0 && numeral.point < 2*representable_digits)
|
154
183
|
near_representable_numeral_to_float numeral
|
155
184
|
elsif numeral.base.modulo(@context.radix) == 0
|
156
185
|
conmensurable_base_numeral_to_float numeral
|
@@ -172,6 +201,12 @@ class Numerals::FloatConversion
|
|
172
201
|
general_numeral_to_float numeral, :free
|
173
202
|
end
|
174
203
|
|
204
|
+
def short_numeral_to_float(numeral)
|
205
|
+
# raise "Invalid Conversion" # Float does not support short (arbitrary precision)
|
206
|
+
# fixed_numeral_to_float numeral
|
207
|
+
general_numeral_to_float numeral, :short
|
208
|
+
end
|
209
|
+
|
175
210
|
def same_base_numeral_to_float(numeral)
|
176
211
|
sign, coefficient, scale = numeral.split
|
177
212
|
@context.Num(sign, coefficient, scale)
|
@@ -203,10 +238,10 @@ class Numerals::FloatConversion
|
|
203
238
|
def general_numeral_to_float(numeral, mode)
|
204
239
|
sign, coefficient, scale = numeral.split
|
205
240
|
reader = Flt::Support::Reader.new(mode: mode)
|
206
|
-
if
|
207
|
-
rounding_mode = @
|
241
|
+
if @input_rounding
|
242
|
+
rounding_mode = @input_rounding.mode
|
208
243
|
else
|
209
|
-
|
244
|
+
rounding_Mode = @context.rounding
|
210
245
|
end
|
211
246
|
reader.read(@context, rounding_mode, sign, coefficient, scale, numeral.base).tap do
|
212
247
|
# @exact = reader.exact?
|
@@ -215,12 +250,12 @@ class Numerals::FloatConversion
|
|
215
250
|
|
216
251
|
end
|
217
252
|
|
218
|
-
def Float.numerals_conversion
|
219
|
-
Numerals::FloatConversion.new
|
253
|
+
def Float.numerals_conversion(options = {})
|
254
|
+
Numerals::FloatConversion.new(options)
|
220
255
|
end
|
221
256
|
|
222
257
|
class <<Float.context
|
223
|
-
def numerals_conversion
|
224
|
-
Numerals::FloatConversion.new
|
258
|
+
def numerals_conversion(options = {})
|
259
|
+
Numerals::FloatConversion.new(options)
|
225
260
|
end
|
226
261
|
end
|
@@ -1,28 +1,20 @@
|
|
1
|
-
require 'numerals/conversions'
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
# @rounding_mode if used for :free numeral to number conversion
|
17
|
-
# and should be the implied rounding mode of the invers conversion
|
18
|
-
# (number to numeral);
|
19
|
-
# TODO: it should be possible to assign it for higher level
|
20
|
-
# formatting handling.
|
21
|
-
@rounding_mode = @context.rounding
|
1
|
+
require 'numerals/conversions/context_conversion'
|
2
|
+
|
3
|
+
class Numerals::FltConversion < Numerals::ContextConversion
|
4
|
+
|
5
|
+
# Options:
|
6
|
+
#
|
7
|
+
# * :input_rounding (optional, a non-exact Rounding or rounding mode)
|
8
|
+
# which is used when input is approximate as the assumed rounding
|
9
|
+
# mode which would be used so that the result numeral rounds back
|
10
|
+
# to the input number. :context can be used to use the
|
11
|
+
# numeric context as input rounding.
|
12
|
+
# input_rounding is also used to round input ...
|
13
|
+
#
|
14
|
+
def initialize(context_or_type, options={})
|
15
|
+
super
|
22
16
|
end
|
23
17
|
|
24
|
-
attr_reader :context, :type
|
25
|
-
|
26
18
|
def order_of_magnitude(value, options={})
|
27
19
|
base = options[:base] || 10 # value.num_class.radix
|
28
20
|
if value.class.radix == base
|
@@ -32,6 +24,19 @@ class Numerals::FltConversion
|
|
32
24
|
end
|
33
25
|
end
|
34
26
|
|
27
|
+
def number_of_digits(value, options={})
|
28
|
+
base = options[:base] || 10
|
29
|
+
if base == @context.radix
|
30
|
+
value.number_of_digits
|
31
|
+
else
|
32
|
+
x.class.context[precision: value.number_of_digits].necessary_digits(base)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def exact?(value, options={})
|
37
|
+
options[:exact]
|
38
|
+
end
|
39
|
+
|
35
40
|
# mode is either :exact or :approximate
|
36
41
|
def number_to_numeral(number, mode, rounding)
|
37
42
|
if number.special? # @context.special?(number)
|
@@ -55,6 +60,54 @@ class Numerals::FltConversion
|
|
55
60
|
end
|
56
61
|
end
|
57
62
|
|
63
|
+
def write(number, exact_input, output_rounding)
|
64
|
+
output_base = output_rounding.base
|
65
|
+
input_base = @context.radix
|
66
|
+
|
67
|
+
if number.special? # @context.special?(number)
|
68
|
+
special_num_to_numeral number
|
69
|
+
elsif exact_input
|
70
|
+
if output_base == input_base && output_rounding.free?
|
71
|
+
# akin to number.format(base: output_base, simplified: true)
|
72
|
+
general_num_to_numeral number, output_rounding, false
|
73
|
+
else
|
74
|
+
# akin to number.format(base: output_base, exact: true)
|
75
|
+
exact_num_to_numeral number, output_rounding
|
76
|
+
end
|
77
|
+
else
|
78
|
+
if output_base == input_base && output_rounding.preserving?
|
79
|
+
# akin to number.format(base: output_base)
|
80
|
+
Numeral.from_coefficient_scale(
|
81
|
+
number.sign*number.coefficient, number.integral_exponent,
|
82
|
+
approximate: true, base: output_base
|
83
|
+
)
|
84
|
+
elsif output_rounding.simplifying?
|
85
|
+
# akin to number.forma(base: output_base, simplify: true)
|
86
|
+
general_num_to_numeral number, output_rounding, false
|
87
|
+
else
|
88
|
+
# akin to number.forma(base: output_base, all_digits: true)
|
89
|
+
general_num_to_numeral number, output_rounding, true
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def read(numeral, exact_input, approximate_simplified)
|
95
|
+
if numeral.special?
|
96
|
+
special_numeral_to_num numeral
|
97
|
+
elsif numeral.approximate? && !exact_input
|
98
|
+
if approximate_simplified
|
99
|
+
# akin to @context.Num(numeral_text, :short)
|
100
|
+
short_numeral_to_num numeral
|
101
|
+
else
|
102
|
+
# akin to @context.Num(numeral_text, :free)
|
103
|
+
free_numeral_to_num numeral
|
104
|
+
end
|
105
|
+
else
|
106
|
+
# akin to @context.Num(numeral_text, :fixed)
|
107
|
+
fixed_numeral_to_num numeral
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
58
111
|
private
|
59
112
|
|
60
113
|
def special_num_to_numeral(x)
|
@@ -67,26 +120,26 @@ class Numerals::FltConversion
|
|
67
120
|
|
68
121
|
def exact_num_to_numeral(number, rounding)
|
69
122
|
quotient = number.to_r
|
70
|
-
numeral = Numeral.from_quotient(quotient, base: rounding.base)
|
71
|
-
unless rounding.
|
123
|
+
numeral = Numerals::Numeral.from_quotient(quotient, base: rounding.base)
|
124
|
+
unless rounding.free?
|
72
125
|
numeral = rounding.round(numeral)
|
73
126
|
end
|
74
127
|
numeral
|
75
128
|
end
|
76
129
|
|
77
130
|
def approximate_num_to_numeral(number, rounding)
|
78
|
-
all_digits = !rounding.
|
131
|
+
all_digits = !rounding.free?
|
79
132
|
general_num_to_numeral(number, rounding, all_digits)
|
80
133
|
end
|
81
134
|
|
82
135
|
def general_num_to_numeral(x, rounding, all_digits)
|
83
|
-
sign, coefficient, exponent = x.split
|
136
|
+
sign, coefficient, exponent = x.split # @context.split(x)
|
84
137
|
precision = x.number_of_digits
|
85
138
|
output_base = rounding.base
|
86
139
|
|
87
|
-
# here rounding_mode
|
88
|
-
|
89
|
-
|
140
|
+
# here rounding_mode is not the output rounding mode, but the rounding mode used for input
|
141
|
+
rounding_mode = (@input_rounding || rounding).mode
|
142
|
+
|
90
143
|
formatter = Flt::Support::Formatter.new(
|
91
144
|
@context.radix, @context.etiny, output_base, raise_on_repeat: false
|
92
145
|
)
|
@@ -96,10 +149,13 @@ class Numerals::FltConversion
|
|
96
149
|
|
97
150
|
dec_pos, digits = formatter.digits
|
98
151
|
rep_pos = formatter.repeat
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
152
|
+
|
153
|
+
normalization = :approximate
|
154
|
+
|
155
|
+
numeral = Numerals::Numeral[digits, sign: sign, point: dec_pos, rep_pos: formatter.repeat, base: output_base, normalize: normalization]
|
156
|
+
|
157
|
+
numeral = rounding.round(numeral, round_up: formatter.round_up)
|
158
|
+
|
103
159
|
numeral
|
104
160
|
end
|
105
161
|
|
@@ -116,10 +172,17 @@ class Numerals::FltConversion
|
|
116
172
|
# consider:
|
117
173
|
# return exact_numeral_to_num(numeral) if numeral.exact?
|
118
174
|
if numeral.base == @context.radix
|
119
|
-
|
175
|
+
unless @context.exact?
|
176
|
+
rounding = Rounding[@context.rounding, precision: @context.precision, base: @context.radix]
|
177
|
+
numeral = rounding.round(numeral)
|
178
|
+
end
|
120
179
|
same_base_numeral_to_num numeral
|
121
180
|
else
|
122
|
-
|
181
|
+
if numeral.repeating? # numeral.exact?
|
182
|
+
exact_numeral_to_num(numeral)
|
183
|
+
else
|
184
|
+
general_numeral_to_num numeral, :fixed
|
185
|
+
end
|
123
186
|
end
|
124
187
|
end
|
125
188
|
|
@@ -133,16 +196,24 @@ class Numerals::FltConversion
|
|
133
196
|
end
|
134
197
|
|
135
198
|
def free_numeral_to_num(numeral)
|
136
|
-
|
199
|
+
if numeral.base == @context.radix
|
200
|
+
same_base_numeral_to_num numeral
|
201
|
+
else
|
202
|
+
general_numeral_to_num numeral, :free
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def short_numeral_to_num(numeral)
|
207
|
+
general_numeral_to_num numeral, :short
|
137
208
|
end
|
138
209
|
|
139
210
|
def general_numeral_to_num(numeral, mode)
|
140
211
|
sign, coefficient, scale = numeral.split
|
141
212
|
reader = Flt::Support::Reader.new(mode: mode)
|
142
|
-
if
|
143
|
-
rounding_mode = @
|
213
|
+
if @input_rounding
|
214
|
+
rounding_mode = @input_rounding.mode
|
144
215
|
else
|
145
|
-
|
216
|
+
rounding_Mode = @context.rounding
|
146
217
|
end
|
147
218
|
reader.read(@context, rounding_mode, sign, coefficient, scale, numeral.base).tap do
|
148
219
|
# @exact = reader.exact?
|
@@ -151,12 +222,12 @@ class Numerals::FltConversion
|
|
151
222
|
|
152
223
|
end
|
153
224
|
|
154
|
-
def (Flt::Num).numerals_conversion
|
155
|
-
Numerals::FltConversion.new(self)
|
225
|
+
def (Flt::Num).numerals_conversion(options = {})
|
226
|
+
Numerals::FltConversion.new(self, options)
|
156
227
|
end
|
157
228
|
|
158
229
|
class Flt::Num::ContextBase
|
159
|
-
def numerals_conversion
|
160
|
-
Numerals::FltConversion.new(self)
|
230
|
+
def numerals_conversion(options = {})
|
231
|
+
Numerals::FltConversion.new(self, options)
|
161
232
|
end
|
162
233
|
end
|
@@ -8,6 +8,10 @@ class Numerals::IntegerConversion
|
|
8
8
|
class InvalidConversion < RuntimeError
|
9
9
|
end
|
10
10
|
|
11
|
+
def type
|
12
|
+
Integer
|
13
|
+
end
|
14
|
+
|
11
15
|
def order_of_magnitude(value, options={})
|
12
16
|
base = options[:base] || 10
|
13
17
|
if base == 2 && value.respond_to?(:bit_length)
|
@@ -17,10 +21,19 @@ class Numerals::IntegerConversion
|
|
17
21
|
end
|
18
22
|
end
|
19
23
|
|
24
|
+
def number_of_digits(value, options={})
|
25
|
+
# order_of_magnitude(value, options)
|
26
|
+
0 # this is needed only for non-exact values
|
27
|
+
end
|
28
|
+
|
29
|
+
def exact?(value, options={})
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
20
33
|
def number_to_numeral(number, mode, rounding)
|
21
34
|
# Rational.numerals_conversion Rational(number), mode, rounding
|
22
|
-
numeral = Numeral.from_quotient(number, 1)
|
23
|
-
numeral = rounding.round(numeral) unless rounding.
|
35
|
+
numeral = Numerals::Numeral.from_quotient(number, 1)
|
36
|
+
numeral = rounding.round(numeral) # unless rounding.free?
|
24
37
|
numeral
|
25
38
|
end
|
26
39
|
|
@@ -32,8 +45,24 @@ class Numerals::IntegerConversion
|
|
32
45
|
rational.numerator
|
33
46
|
end
|
34
47
|
|
48
|
+
def write(number, exact_input, output_rounding)
|
49
|
+
output_base = output_rounding.base
|
50
|
+
numeral = Numerals::Numeral.from_quotient(number, 1, base: output_base)
|
51
|
+
numeral = output_rounding.round(numeral) # unless output_rounding.free?
|
52
|
+
numeral
|
53
|
+
end
|
54
|
+
|
55
|
+
def read(numeral, exact_input, approximate_simplified)
|
56
|
+
rational = Rational.numerals_conversion.read numeral, exact_input, approximate_simplified
|
57
|
+
if rational.denominator != 1
|
58
|
+
raise InvalidConversion, "Invalid numeral to rational conversion"
|
59
|
+
end
|
60
|
+
rational.numerator
|
61
|
+
end
|
62
|
+
|
63
|
+
|
35
64
|
end
|
36
65
|
|
37
|
-
def Integer.numerals_conversion
|
66
|
+
def Integer.numerals_conversion(options = {})
|
38
67
|
Numerals::IntegerConversion.instance
|
39
68
|
end
|
@@ -5,6 +5,10 @@ class Numerals::RationalConversion
|
|
5
5
|
|
6
6
|
include Singleton
|
7
7
|
|
8
|
+
def type
|
9
|
+
Rational
|
10
|
+
end
|
11
|
+
|
8
12
|
def order_of_magnitude(value, options={})
|
9
13
|
base = options[:base] || 10
|
10
14
|
if base == 10
|
@@ -14,10 +18,18 @@ class Numerals::RationalConversion
|
|
14
18
|
end
|
15
19
|
end
|
16
20
|
|
21
|
+
def number_of_digits(value, options={})
|
22
|
+
return 0 # this is needed only for non-exact values
|
23
|
+
end
|
24
|
+
|
25
|
+
def exact?(value, options={})
|
26
|
+
true
|
27
|
+
end
|
28
|
+
|
17
29
|
def number_to_numeral(number, mode, rounding)
|
18
30
|
q = [number.numerator, number.denominator]
|
19
|
-
numeral = Numeral.from_quotient(q)
|
20
|
-
numeral = rounding.round(numeral) unless rounding.
|
31
|
+
numeral = Numerals::Numeral.from_quotient(q)
|
32
|
+
numeral = rounding.round(numeral) # unless rounding.free?
|
21
33
|
numeral
|
22
34
|
end
|
23
35
|
|
@@ -25,8 +37,20 @@ class Numerals::RationalConversion
|
|
25
37
|
Rational(*numeral.to_quotient)
|
26
38
|
end
|
27
39
|
|
40
|
+
def write(number, exact_input, output_rounding)
|
41
|
+
output_base = output_rounding.base
|
42
|
+
q = [number.numerator, number.denominator]
|
43
|
+
numeral = Numerals::Numeral.from_quotient(q, base: output_base)
|
44
|
+
numeral = output_rounding.round(numeral) # unless output_rounding.free?
|
45
|
+
numeral
|
46
|
+
end
|
47
|
+
|
48
|
+
def read(numeral, exact_input, approximate_simplified)
|
49
|
+
Rational(*numeral.to_quotient)
|
50
|
+
end
|
51
|
+
|
28
52
|
end
|
29
53
|
|
30
|
-
def Rational.numerals_conversion
|
54
|
+
def Rational.numerals_conversion(options = {})
|
31
55
|
Numerals::RationalConversion.instance
|
32
56
|
end
|