numerals 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/numerals.svg)](http://badge.fury.io/rb/numerals)
|
5
|
+
[![Build Status](https://travis-ci.org/jgoizueta/numerals.svg)](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
|