numerals 0.2.0 → 0.2.1
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 +3 -0
- data/Rakefile +2 -0
- data/lib/numerals.rb +0 -1
- data/lib/numerals/conversions.rb +88 -84
- data/lib/numerals/conversions/context_conversion.rb +30 -26
- data/lib/numerals/conversions/flt.rb +185 -181
- data/lib/numerals/format/base_scaler.rb +2 -2
- data/lib/numerals/format/input.rb +1 -1
- data/lib/numerals/format/notations/html.rb +1 -1
- data/lib/numerals/version.rb +1 -1
- data/test/test_base_scaler.rb +2 -1
- data/test/test_big_conversions.rb +1 -1
- data/test/test_digits_definition.rb +2 -1
- data/test/test_exp_setter.rb +2 -1
- data/test/test_float_conversions.rb +1 -1
- data/test/test_flt_conversions.rb +2 -1
- data/test/test_format.rb +2 -1
- data/test/test_format_input.rb +2 -1
- data/test/test_format_mode.rb +2 -1
- data/test/test_format_output.rb +2 -1
- data/test/test_integer_conversions.rb +2 -1
- data/test/test_numeral.rb +2 -1
- data/test/test_qualified.rb +13 -0
- data/test/test_rational_conversions.rb +2 -1
- data/test/test_repeat_detector.rb +2 -1
- data/test/test_rounding.rb +2 -1
- data/test/test_symbols.rb +2 -1
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c2db2456e26dc108b6ffc0e72616b58c94c00eee
|
4
|
+
data.tar.gz: 4150c883baaa50b85c7f9f7619b5bde35ec5ff9e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 755fe267a26a4091942e522f32e335cc5d17adfcd3d24a671a09d251927d40bdf7a2fc8f508f27272de8b1dd310ac160d7bf256079d7c9b44df34bb542724661
|
7
|
+
data.tar.gz: e1d04a258486962b14b0c5c1065d017a20ea09bcb15d551579a55f0db3f0a206ebe7b800ce065ca7e9ca0aff832ecb2a0d357b4390228ed7caa5931b684e4d5c
|
data/README.md
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
Numerals
|
2
2
|
========
|
3
3
|
|
4
|
+
[](http://badge.fury.io/rb/numerals)
|
5
|
+
[](https://travis-ci.org/jgoizueta/numerals)
|
6
|
+
|
4
7
|
The Numerals module provides formatted input/output for numeric types.
|
5
8
|
|
6
9
|
## Use
|
data/Rakefile
CHANGED
data/lib/numerals.rb
CHANGED
data/lib/numerals/conversions.rb
CHANGED
@@ -1,98 +1,102 @@
|
|
1
|
-
module Numerals
|
1
|
+
module Numerals
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
module Conversions
|
4
|
+
|
5
|
+
class <<self
|
6
|
+
def [](type, options = nil)
|
7
|
+
if type.respond_to?(:numerals_conversion)
|
8
|
+
type.numerals_conversion(options || {})
|
9
|
+
end
|
7
10
|
end
|
8
|
-
end
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
|
12
|
+
def order_of_magnitude(number, options={})
|
13
|
+
self[number.class, options[:type_options]].order_of_magnitude(number, options)
|
14
|
+
end
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
-
|
16
|
+
def number_of_digits(number, options={})
|
17
|
+
self[number.class, options[:type_options]].number_of_digits(number, options)
|
18
|
+
end
|
17
19
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
20
|
+
# Convert Numeral to Number
|
21
|
+
#
|
22
|
+
# read numeral, options={}
|
23
|
+
#
|
24
|
+
# If the input numeral is approximate and the destination type
|
25
|
+
# allows for arbitrary precision, then the destination context
|
26
|
+
# precision will be ignored and the precision of the input will be
|
27
|
+
# preserved. The :simplify option affects this case by generating
|
28
|
+
# only the mininimun number of digits needed.
|
29
|
+
#
|
30
|
+
# The :exact option will prevent this behaviour and always treat
|
31
|
+
# input as exact.
|
32
|
+
#
|
33
|
+
# Valid output options:
|
34
|
+
#
|
35
|
+
# * :type class of the output number
|
36
|
+
# * :context context (in the case of Flt::Num, Float) for the output
|
37
|
+
# * :simplify (for approximate input numeral/arbitrary precision type only)
|
38
|
+
# * :exact treat input numeral as if exact
|
39
|
+
#
|
40
|
+
def read(numeral, options={})
|
41
|
+
selector = options[:context] || options[:type]
|
42
|
+
exact_input = options[:exact]
|
43
|
+
approximate_simplified = options[:simplify]
|
44
|
+
conversions = self[selector, options[:type_options]]
|
45
|
+
conversions.read(numeral, exact_input, approximate_simplified)
|
46
|
+
end
|
45
47
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
48
|
+
# Convert Number to Numeral
|
49
|
+
#
|
50
|
+
# write number, options={}
|
51
|
+
#
|
52
|
+
# Valid options:
|
53
|
+
#
|
54
|
+
# * :rounding (a Rounding) (which defines output base as well)
|
55
|
+
# * :exact (exact input indicator)
|
56
|
+
#
|
57
|
+
# Approximate mode:
|
58
|
+
#
|
59
|
+
# If the input is treated as an approximation
|
60
|
+
# (which is the case for types such as Flt::Num, Float,...
|
61
|
+
# unless the :exact option is true) then no 'spurious' digits
|
62
|
+
# will be shown (digits that can take any value and the numeral
|
63
|
+
# still would convert to the original number if rounded to the same precision)
|
64
|
+
#
|
65
|
+
# In approximate mode, if rounding is simplifying? (:short), the shortest representation
|
66
|
+
# which rounds back to the origina number with the same precision is used.
|
67
|
+
# If rounding is :free and the output base is the same as the number
|
68
|
+
# internal radix, the exact precision (trailing zeros) of the number
|
69
|
+
# is represented.
|
70
|
+
#
|
71
|
+
# Exact mode:
|
72
|
+
#
|
73
|
+
# Is used for 'exact' types (such as Integer, Rational) or when the :exact
|
74
|
+
# option is defined to be true.
|
75
|
+
#
|
76
|
+
# The number is treated as an exact value, and converted according to
|
77
|
+
# Rounding. (in this case the :free and :short precision roundings are
|
78
|
+
# equivalent)
|
79
|
+
#
|
80
|
+
def write(number, options = {})
|
81
|
+
output_rounding = Rounding[options[:rounding] || Rounding[]]
|
82
|
+
conversion = self[number.class, options[:type_options]]
|
83
|
+
exact_input = conversion.exact?(number, options)
|
84
|
+
conversion.write(number, exact_input, output_rounding)
|
85
|
+
end
|
84
86
|
|
85
|
-
|
86
|
-
|
87
|
-
|
87
|
+
def exact?(number, options = {})
|
88
|
+
self[number.class, options[:type_options]].exact?(number, options)
|
89
|
+
end
|
88
90
|
|
89
|
-
|
91
|
+
private
|
90
92
|
|
91
|
-
|
92
|
-
|
93
|
-
|
93
|
+
def extract_mode_from_args!(args)
|
94
|
+
if [:fixed, :free, :exact, :approximate].include?(args.first)
|
95
|
+
args.shift
|
96
|
+
end
|
94
97
|
end
|
95
98
|
end
|
99
|
+
|
96
100
|
end
|
97
101
|
|
98
102
|
end
|
@@ -1,40 +1,44 @@
|
|
1
1
|
require 'numerals/conversions'
|
2
2
|
require 'flt'
|
3
3
|
|
4
|
-
|
5
|
-
class Numerals::ContextConversion
|
4
|
+
module Numerals
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
6
|
+
# Base class for Conversions of type with context
|
7
|
+
class ContextConversion
|
8
|
+
|
9
|
+
def initialize(context_or_type, options={})
|
10
|
+
if Class === context_or_type && context_or_type.respond_to?(:context)
|
11
|
+
@type = context_or_type
|
12
|
+
@context = @type.context
|
13
|
+
elsif context_or_type.respond_to?(:num_class)
|
14
|
+
@context = context_or_type
|
15
|
+
@type = @context.num_class
|
16
|
+
else
|
17
|
+
raise "Invalid Conversion definition"
|
18
|
+
end
|
19
|
+
self.input_rounding = options[:input_rounding]
|
16
20
|
end
|
17
|
-
self.input_rounding = options[:input_rounding]
|
18
|
-
end
|
19
21
|
|
20
|
-
|
22
|
+
attr_reader :context, :type, :input_rounding
|
21
23
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
else
|
27
|
-
rounding = Rounding[base: @context.radix].set!(rounding)
|
28
|
-
if rounding.base == @context.radix
|
29
|
-
@input_rounding = rounding
|
24
|
+
def input_rounding=(rounding)
|
25
|
+
if rounding
|
26
|
+
if rounding == :context
|
27
|
+
@input_rounding = Rounding[@context.rounding, precision: @context.precision, base: @context.radix]
|
30
28
|
else
|
31
|
-
|
32
|
-
|
29
|
+
rounding = Rounding[base: @context.radix].set!(rounding)
|
30
|
+
if rounding.base == @context.radix
|
31
|
+
@input_rounding = rounding
|
32
|
+
else
|
33
|
+
# The rounding precision is not meaningful for the destination type on input
|
34
|
+
@input_rounding = Rounding[rounding.mode, base: @context.radix]
|
35
|
+
end
|
33
36
|
end
|
37
|
+
else
|
38
|
+
@input_rounding = nil
|
34
39
|
end
|
35
|
-
else
|
36
|
-
@input_rounding = nil
|
37
40
|
end
|
41
|
+
|
38
42
|
end
|
39
43
|
|
40
44
|
end
|
@@ -1,233 +1,237 @@
|
|
1
1
|
require 'numerals/conversions/context_conversion'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
def order_of_magnitude(value, options={})
|
19
|
-
base = options[:base] || 10 # value.num_class.radix
|
20
|
-
if value.class.radix == base
|
21
|
-
value.adjusted_exponent + 1
|
22
|
-
else
|
23
|
-
value.abs.log(base).floor + 1
|
3
|
+
module Numerals
|
4
|
+
|
5
|
+
class FltConversion < ContextConversion
|
6
|
+
|
7
|
+
# Options:
|
8
|
+
#
|
9
|
+
# * :input_rounding (optional, a non-exact Rounding or rounding mode)
|
10
|
+
# which is used when input is approximate as the assumed rounding
|
11
|
+
# mode which would be used so that the result numeral rounds back
|
12
|
+
# to the input number. :context can be used to use the
|
13
|
+
# numeric context as input rounding.
|
14
|
+
# input_rounding is also used to round input ...
|
15
|
+
#
|
16
|
+
def initialize(context_or_type, options={})
|
17
|
+
super
|
24
18
|
end
|
25
|
-
end
|
26
19
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
20
|
+
def order_of_magnitude(value, options={})
|
21
|
+
base = options[:base] || 10 # value.num_class.radix
|
22
|
+
if value.class.radix == base
|
23
|
+
value.adjusted_exponent + 1
|
24
|
+
else
|
25
|
+
value.abs.log(base).floor + 1
|
26
|
+
end
|
33
27
|
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def exact?(value, options={})
|
37
|
-
options[:exact]
|
38
|
-
end
|
39
28
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
exact_num_to_numeral number, rounding
|
47
|
-
else # mode == :approximate
|
48
|
-
approximate_num_to_numeral(number, rounding)
|
29
|
+
def number_of_digits(value, options={})
|
30
|
+
base = options[:base] || 10
|
31
|
+
if base == @context.radix
|
32
|
+
value.number_of_digits
|
33
|
+
else
|
34
|
+
x.class.context[precision: value.number_of_digits].necessary_digits(base)
|
49
35
|
end
|
50
36
|
end
|
51
|
-
end
|
52
37
|
|
53
|
-
|
54
|
-
|
55
|
-
special_numeral_to_num numeral
|
56
|
-
elsif mode == :fixed
|
57
|
-
fixed_numeral_to_num numeral
|
58
|
-
else # mode == :free
|
59
|
-
free_numeral_to_num numeral
|
38
|
+
def exact?(value, options={})
|
39
|
+
options[:exact]
|
60
40
|
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def write(number, exact_input, output_rounding)
|
64
|
-
output_base = output_rounding.base
|
65
|
-
input_base = @context.radix
|
66
41
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
# akin to number.format(base: output_base, simplified: true)
|
72
|
-
general_num_to_numeral number, output_rounding, false
|
42
|
+
# mode is either :exact or :approximate
|
43
|
+
def number_to_numeral(number, mode, rounding)
|
44
|
+
if number.special? # @context.special?(number)
|
45
|
+
special_num_to_numeral(number)
|
73
46
|
else
|
74
|
-
|
75
|
-
|
47
|
+
if mode == :exact
|
48
|
+
exact_num_to_numeral number, rounding
|
49
|
+
else # mode == :approximate
|
50
|
+
approximate_num_to_numeral(number, rounding)
|
51
|
+
end
|
76
52
|
end
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
53
|
+
end
|
54
|
+
|
55
|
+
def numeral_to_number(numeral, mode)
|
56
|
+
if numeral.special?
|
57
|
+
special_numeral_to_num numeral
|
58
|
+
elsif mode == :fixed
|
59
|
+
fixed_numeral_to_num numeral
|
60
|
+
else # mode == :free
|
61
|
+
free_numeral_to_num numeral
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def write(number, exact_input, output_rounding)
|
66
|
+
output_base = output_rounding.base
|
67
|
+
input_base = @context.radix
|
68
|
+
|
69
|
+
if number.special? # @context.special?(number)
|
70
|
+
special_num_to_numeral number
|
71
|
+
elsif exact_input
|
72
|
+
if output_base == input_base && output_rounding.free?
|
73
|
+
# akin to number.format(base: output_base, simplified: true)
|
74
|
+
general_num_to_numeral number, output_rounding, false
|
75
|
+
else
|
76
|
+
# akin to number.format(base: output_base, exact: true)
|
77
|
+
exact_num_to_numeral number, output_rounding
|
78
|
+
end
|
87
79
|
else
|
88
|
-
|
89
|
-
|
80
|
+
if output_base == input_base && output_rounding.preserving?
|
81
|
+
# akin to number.format(base: output_base)
|
82
|
+
Numeral.from_coefficient_scale(
|
83
|
+
number.sign*number.coefficient, number.integral_exponent,
|
84
|
+
approximate: true, base: output_base
|
85
|
+
)
|
86
|
+
elsif output_rounding.simplifying?
|
87
|
+
# akin to number.forma(base: output_base, simplify: true)
|
88
|
+
general_num_to_numeral number, output_rounding, false
|
89
|
+
else
|
90
|
+
# akin to number.forma(base: output_base, all_digits: true)
|
91
|
+
general_num_to_numeral number, output_rounding, true
|
92
|
+
end
|
90
93
|
end
|
91
94
|
end
|
92
|
-
end
|
93
95
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
96
|
+
def read(numeral, exact_input, approximate_simplified)
|
97
|
+
if numeral.special?
|
98
|
+
special_numeral_to_num numeral
|
99
|
+
elsif numeral.approximate? && !exact_input
|
100
|
+
if approximate_simplified
|
101
|
+
# akin to @context.Num(numeral_text, :short)
|
102
|
+
short_numeral_to_num numeral
|
103
|
+
else
|
104
|
+
# akin to @context.Num(numeral_text, :free)
|
105
|
+
free_numeral_to_num numeral
|
106
|
+
end
|
101
107
|
else
|
102
|
-
# akin to @context.Num(numeral_text, :
|
103
|
-
|
108
|
+
# akin to @context.Num(numeral_text, :fixed)
|
109
|
+
fixed_numeral_to_num numeral
|
104
110
|
end
|
105
|
-
else
|
106
|
-
# akin to @context.Num(numeral_text, :fixed)
|
107
|
-
fixed_numeral_to_num numeral
|
108
111
|
end
|
109
|
-
end
|
110
112
|
|
111
|
-
|
113
|
+
private
|
112
114
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
115
|
+
def special_num_to_numeral(x)
|
116
|
+
if x.nan?
|
117
|
+
Numeral.nan
|
118
|
+
elsif x.infinite?
|
119
|
+
Numeral.infinity @context.sign(x)
|
120
|
+
end
|
118
121
|
end
|
119
|
-
end
|
120
122
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
123
|
+
def exact_num_to_numeral(number, rounding)
|
124
|
+
quotient = number.to_r
|
125
|
+
numeral = Numerals::Numeral.from_quotient(quotient, base: rounding.base)
|
126
|
+
unless rounding.free?
|
127
|
+
numeral = rounding.round(numeral)
|
128
|
+
end
|
129
|
+
numeral
|
126
130
|
end
|
127
|
-
numeral
|
128
|
-
end
|
129
131
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
132
|
+
def approximate_num_to_numeral(number, rounding)
|
133
|
+
all_digits = !rounding.free?
|
134
|
+
general_num_to_numeral(number, rounding, all_digits)
|
135
|
+
end
|
134
136
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
137
|
+
def general_num_to_numeral(x, rounding, all_digits)
|
138
|
+
sign, coefficient, exponent = x.split # @context.split(x)
|
139
|
+
precision = x.number_of_digits
|
140
|
+
output_base = rounding.base
|
139
141
|
|
140
|
-
|
141
|
-
|
142
|
+
# here rounding_mode is not the output rounding mode, but the rounding mode used for input
|
143
|
+
rounding_mode = (@input_rounding || rounding).mode
|
142
144
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
145
|
+
formatter = Flt::Support::Formatter.new(
|
146
|
+
@context.radix, @context.etiny, output_base, raise_on_repeat: false
|
147
|
+
)
|
148
|
+
formatter.format(
|
149
|
+
x, coefficient, exponent, rounding_mode, precision, all_digits
|
150
|
+
)
|
149
151
|
|
150
|
-
|
151
|
-
|
152
|
+
dec_pos, digits = formatter.digits
|
153
|
+
rep_pos = formatter.repeat
|
152
154
|
|
153
|
-
|
155
|
+
normalization = :approximate
|
154
156
|
|
155
|
-
|
157
|
+
numeral = Numerals::Numeral[digits, sign: sign, point: dec_pos, rep_pos: formatter.repeat, base: output_base, normalize: normalization]
|
156
158
|
|
157
|
-
|
159
|
+
numeral = rounding.round(numeral, round_up: formatter.round_up)
|
158
160
|
|
159
|
-
|
160
|
-
end
|
161
|
-
|
162
|
-
def special_numeral_to_num(numeral)
|
163
|
-
case numeral.special
|
164
|
-
when :nan
|
165
|
-
@context.nan
|
166
|
-
when :inf
|
167
|
-
@context.infinity numeral.sign
|
161
|
+
numeral
|
168
162
|
end
|
169
|
-
end
|
170
163
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
numeral = rounding.round(numeral)
|
164
|
+
def special_numeral_to_num(numeral)
|
165
|
+
case numeral.special
|
166
|
+
when :nan
|
167
|
+
@context.nan
|
168
|
+
when :inf
|
169
|
+
@context.infinity numeral.sign
|
178
170
|
end
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
171
|
+
end
|
172
|
+
|
173
|
+
def fixed_numeral_to_num(numeral)
|
174
|
+
# consider:
|
175
|
+
# return exact_numeral_to_num(numeral) if numeral.exact?
|
176
|
+
if numeral.base == @context.radix
|
177
|
+
unless @context.exact?
|
178
|
+
rounding = Rounding[@context.rounding, precision: @context.precision, base: @context.radix]
|
179
|
+
numeral = rounding.round(numeral)
|
180
|
+
end
|
181
|
+
same_base_numeral_to_num numeral
|
183
182
|
else
|
184
|
-
|
183
|
+
if numeral.repeating? # numeral.exact?
|
184
|
+
exact_numeral_to_num(numeral)
|
185
|
+
else
|
186
|
+
general_numeral_to_num numeral, :fixed
|
187
|
+
end
|
185
188
|
end
|
186
189
|
end
|
187
|
-
end
|
188
|
-
|
189
|
-
def same_base_numeral_to_num(numeral)
|
190
|
-
sign, coefficient, scale = numeral.split
|
191
|
-
@context.Num sign, coefficient, scale
|
192
|
-
end
|
193
|
-
|
194
|
-
def exact_numeral_to_num(numeral)
|
195
|
-
@context.Num Rational(*numeral.to_quotient), :fixed
|
196
|
-
end
|
197
190
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
else
|
202
|
-
general_numeral_to_num numeral, :free
|
191
|
+
def same_base_numeral_to_num(numeral)
|
192
|
+
sign, coefficient, scale = numeral.split
|
193
|
+
@context.Num sign, coefficient, scale
|
203
194
|
end
|
204
|
-
end
|
205
195
|
|
206
|
-
|
207
|
-
|
208
|
-
|
196
|
+
def exact_numeral_to_num(numeral)
|
197
|
+
@context.Num Rational(*numeral.to_quotient), :fixed
|
198
|
+
end
|
209
199
|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
rounding_Mode = @context.rounding
|
200
|
+
def free_numeral_to_num(numeral)
|
201
|
+
if numeral.base == @context.radix
|
202
|
+
same_base_numeral_to_num numeral
|
203
|
+
else
|
204
|
+
general_numeral_to_num numeral, :free
|
205
|
+
end
|
217
206
|
end
|
218
|
-
|
219
|
-
|
207
|
+
|
208
|
+
def short_numeral_to_num(numeral)
|
209
|
+
general_numeral_to_num numeral, :short
|
220
210
|
end
|
221
|
-
end
|
222
211
|
|
223
|
-
|
212
|
+
def general_numeral_to_num(numeral, mode)
|
213
|
+
sign, coefficient, scale = numeral.split
|
214
|
+
reader = Flt::Support::Reader.new(mode: mode)
|
215
|
+
if @input_rounding
|
216
|
+
rounding_mode = @input_rounding.mode
|
217
|
+
else
|
218
|
+
rounding_Mode = @context.rounding
|
219
|
+
end
|
220
|
+
reader.read(@context, rounding_mode, sign, coefficient, scale, numeral.base).tap do
|
221
|
+
# @exact = reader.exact?
|
222
|
+
end
|
223
|
+
end
|
224
224
|
|
225
|
-
|
226
|
-
Numerals::FltConversion.new(self, options)
|
227
|
-
end
|
225
|
+
end
|
228
226
|
|
229
|
-
|
230
|
-
def numerals_conversion(options = {})
|
227
|
+
def (Flt::Num).numerals_conversion(options = {})
|
231
228
|
Numerals::FltConversion.new(self, options)
|
232
229
|
end
|
230
|
+
|
231
|
+
class Flt::Num::ContextBase
|
232
|
+
def numerals_conversion(options = {})
|
233
|
+
Numerals::FltConversion.new(self, options)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
233
237
|
end
|
@@ -144,9 +144,9 @@ module Numerals
|
|
144
144
|
end
|
145
145
|
|
146
146
|
# Convert base digits to scaled base digits
|
147
|
-
def self.
|
147
|
+
def self.ungrouped_digits(digits, base, base_scale)
|
148
148
|
digits.flat_map { |d|
|
149
|
-
group =
|
149
|
+
group = Digits[base: base]
|
150
150
|
group.value = d
|
151
151
|
ungrouped = group.digits_array
|
152
152
|
if ungrouped.size < base_scale
|
@@ -104,7 +104,7 @@ module Numerals
|
|
104
104
|
|
105
105
|
if @mode.base_scale > 1
|
106
106
|
# De-scale the significand base
|
107
|
-
digits = Format::BaseScaler.
|
107
|
+
digits = Format::BaseScaler.ungrouped_digits(digits, base, @mode.base_scale)
|
108
108
|
point *= @mode.base_scale
|
109
109
|
repeat *= @mode.base_scale if repeat
|
110
110
|
end
|
@@ -34,7 +34,7 @@ module Numerals
|
|
34
34
|
else
|
35
35
|
# show base suffix as a subscript
|
36
36
|
subscript = format.symbols.base_suffix || base.to_s
|
37
|
-
|
37
|
+
output << "<sub>#{subscript}</sub>"
|
38
38
|
end
|
39
39
|
end
|
40
40
|
if text_parts.exponent_value != 0 || format.mode.mode == :scientific
|
data/lib/numerals/version.rb
CHANGED
data/test/test_base_scaler.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__),'helper.rb'))
|
2
2
|
|
3
3
|
require 'numerals'
|
4
|
-
include Numerals
|
5
4
|
|
6
5
|
class TestBaseScaler < Test::Unit::TestCase # < Minitest::Test
|
7
6
|
|
7
|
+
include Numerals
|
8
|
+
|
8
9
|
def digits_string(part, base)
|
9
10
|
part.map{|d| d.to_s(base)}.join
|
10
11
|
end
|
@@ -2,10 +2,10 @@ require File.expand_path(File.join(File.dirname(__FILE__),'helper.rb'))
|
|
2
2
|
|
3
3
|
require 'numerals'
|
4
4
|
require 'flt/bigdecimal'
|
5
|
-
include Numerals
|
6
5
|
|
7
6
|
class TestBigConversions < Test::Unit::TestCase # < Minitest::Test
|
8
7
|
|
8
|
+
include Numerals
|
9
9
|
|
10
10
|
def test_write_special
|
11
11
|
context = BigDecimal.context
|
@@ -2,11 +2,12 @@
|
|
2
2
|
|
3
3
|
require File.expand_path(File.join(File.dirname(__FILE__),'helper.rb'))
|
4
4
|
require 'test/unit'
|
5
|
-
include Numerals
|
6
5
|
require 'yaml'
|
7
6
|
|
8
7
|
class TestDigitsDefinition < Test::Unit::TestCase
|
9
8
|
|
9
|
+
include Numerals
|
10
|
+
|
10
11
|
DEFAULT_DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
11
12
|
MAX_TEST_BASE = DEFAULT_DIGITS.size
|
12
13
|
|
data/test/test_exp_setter.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__),'helper.rb'))
|
2
2
|
|
3
3
|
require 'numerals'
|
4
|
-
include Numerals
|
5
4
|
|
6
5
|
class TestExpSetter < Test::Unit::TestCase # < Minitest::Test
|
7
6
|
|
7
|
+
include Numerals
|
8
|
+
|
8
9
|
def check_setter(numeral, n=nil)
|
9
10
|
adjust = Format::ExpSetter[numeral]
|
10
11
|
adjust.integer_part_size = n if n
|
@@ -1,10 +1,10 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__),'helper.rb'))
|
2
2
|
|
3
3
|
require 'numerals'
|
4
|
-
include Numerals
|
5
4
|
|
6
5
|
class TestFloatConversions < Test::Unit::TestCase # < Minitest::Test
|
7
6
|
|
7
|
+
include Numerals
|
8
8
|
|
9
9
|
def test_write_special
|
10
10
|
assert_equal Numeral.nan, Conversions.write(Float.context.nan)
|
@@ -1,10 +1,11 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__),'helper.rb'))
|
2
2
|
|
3
3
|
require 'numerals'
|
4
|
-
include Numerals
|
5
4
|
|
6
5
|
class TestFltConversions < Test::Unit::TestCase # < Minitest::Test
|
7
6
|
|
7
|
+
include Numerals
|
8
|
+
|
8
9
|
def test_write_special_binary
|
9
10
|
context = Flt::BinNum.context = Flt::BinNum::FloatContext
|
10
11
|
type = Flt::BinNum
|
data/test/test_format.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__),'helper.rb'))
|
2
|
-
include Numerals
|
3
2
|
require 'yaml'
|
4
3
|
|
5
4
|
class TestFormat < Test::Unit::TestCase # < Minitest::Test
|
6
5
|
|
6
|
+
include Numerals
|
7
|
+
|
7
8
|
def test_mutated_copy
|
8
9
|
f1 = Format[Rounding[precision: 3, base: 2]]
|
9
10
|
|
data/test/test_format_input.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__),'helper.rb'))
|
2
|
-
include Numerals
|
3
2
|
require 'yaml'
|
4
3
|
|
5
4
|
class TestFormatInput < Test::Unit::TestCase # < Minitest::Test
|
6
5
|
|
6
|
+
include Numerals
|
7
|
+
|
7
8
|
def assert_same_flt(x, y)
|
8
9
|
assert_equal x.class, y.class, x.to_s
|
9
10
|
assert_equal x.split, y.split, x.to_s
|
data/test/test_format_mode.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__),'helper.rb'))
|
2
2
|
|
3
3
|
require 'numerals/rounding'
|
4
|
-
include Numerals
|
5
4
|
|
6
5
|
class TestFormatMode < Test::Unit::TestCase # < Minitest::Test
|
7
6
|
|
7
|
+
include Numerals
|
8
|
+
|
8
9
|
def test_format_mode_constructor
|
9
10
|
mode = Format::Mode[:scientific]
|
10
11
|
assert mode.scientific?
|
data/test/test_format_output.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__),'helper.rb'))
|
2
|
-
include Numerals
|
3
2
|
require 'yaml'
|
4
3
|
require 'tempfile'
|
5
4
|
|
6
5
|
class TestFormatOutput < Test::Unit::TestCase # < Minitest::Test
|
7
6
|
|
7
|
+
include Numerals
|
8
|
+
|
8
9
|
def test_write_float_dec
|
9
10
|
assert_equal '1', Format[rounding: :short].write(1.0)
|
10
11
|
assert_equal '1', Format[].write(1.0)
|
@@ -1,10 +1,11 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__),'helper.rb'))
|
2
2
|
|
3
3
|
require 'numerals'
|
4
|
-
include Numerals
|
5
4
|
|
6
5
|
class TestIntegerConversions < Test::Unit::TestCase # < Minitest::Test
|
7
6
|
|
7
|
+
include Numerals
|
8
|
+
|
8
9
|
def test_read_special
|
9
10
|
assert_raise(ZeroDivisionError){ Conversions.read(Numeral.nan, type: Integer) }
|
10
11
|
assert_raise(ZeroDivisionError){ Conversions.read(Numeral.infinity, type: Integer) }
|
data/test/test_numeral.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__),'helper.rb'))
|
2
|
-
include Numerals
|
3
2
|
require 'yaml'
|
4
3
|
|
5
4
|
class TestNumeral < Test::Unit::TestCase # < Minitest::Test
|
6
5
|
|
6
|
+
include Numerals
|
7
|
+
|
7
8
|
def test_numeral_reference_constructors
|
8
9
|
# We'll use this forms as reference for comparisons:
|
9
10
|
# Numeral[digits, base: ..., point: ... , repeat: ...]
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__),'helper.rb'))
|
2
|
+
|
3
|
+
class TestQualified < Test::Unit::TestCase # < Minitest::Test
|
4
|
+
|
5
|
+
def test_qualified_use
|
6
|
+
assert_equal '1', Numerals::Format[].write(1.0)
|
7
|
+
assert_equal '1.00', Numerals::Format[Numerals::Rounding[precision: 3]].write(1.0)
|
8
|
+
assert_equal '1.000', Numerals::Format[Numerals::Rounding[places: 3]].write(1.0)
|
9
|
+
assert_equal '1234567.1234', Numerals::Format[rounding: :short].write(1234567.1234)
|
10
|
+
assert_equal '1234567.123', Numerals::Format[Numerals::Rounding[places: 3]].write(1234567.1234)
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
@@ -1,10 +1,11 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__),'helper.rb'))
|
2
2
|
|
3
3
|
require 'numerals'
|
4
|
-
include Numerals
|
5
4
|
|
6
5
|
class TestRationalConversions < Test::Unit::TestCase # < Minitest::Test
|
7
6
|
|
7
|
+
include Numerals
|
8
|
+
|
8
9
|
def test_read_special
|
9
10
|
assert_raise(ZeroDivisionError){ Conversions.read(Numeral.nan, type: Rational) }
|
10
11
|
assert_raise(ZeroDivisionError){ Conversions.read(Numeral.infinity, type: Rational) }
|
data/test/test_rounding.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__),'helper.rb'))
|
2
2
|
|
3
3
|
require 'numerals/rounding'
|
4
|
-
include Numerals
|
5
4
|
|
6
5
|
class TestRounding < Test::Unit::TestCase # < Minitest::Test
|
7
6
|
|
7
|
+
include Numerals
|
8
|
+
|
8
9
|
def test_rounding
|
9
10
|
r = Rounding[:half_even, places: 0]
|
10
11
|
assert_equal Numeral[1,0,0, point: 3, normalize: :approximate], r.round(Numeral[1,0,0,5, point: 3])
|
data/test/test_symbols.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__),'helper.rb'))
|
2
2
|
|
3
3
|
require 'numerals/rounding'
|
4
|
-
include Numerals
|
5
4
|
|
6
5
|
class TestSymbols < Test::Unit::TestCase # < Minitest::Test
|
7
6
|
|
7
|
+
include Numerals
|
8
|
+
|
8
9
|
def test_symbols
|
9
10
|
s = Format::Symbols[show_plus: false]
|
10
11
|
s2 = s[uppercase: true]
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: numerals
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Javier Goizueta
|
@@ -123,6 +123,7 @@ files:
|
|
123
123
|
- test/test_format_output.rb
|
124
124
|
- test/test_integer_conversions.rb
|
125
125
|
- test/test_numeral.rb
|
126
|
+
- test/test_qualified.rb
|
126
127
|
- test/test_rational_conversions.rb
|
127
128
|
- test/test_repeat_detector.rb
|
128
129
|
- test/test_rounding.rb
|
@@ -166,6 +167,7 @@ test_files:
|
|
166
167
|
- test/test_format_output.rb
|
167
168
|
- test/test_integer_conversions.rb
|
168
169
|
- test/test_numeral.rb
|
170
|
+
- test/test_qualified.rb
|
169
171
|
- test/test_rational_conversions.rb
|
170
172
|
- test/test_repeat_detector.rb
|
171
173
|
- test/test_rounding.rb
|