fixed 1.0.0 → 1.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 +3 -12
- data/lib/fixed/version.rb +7 -1
- data/lib/fixed.rb +61 -20
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 61a3482844d4fbd66a1edb0dee9b686ba6a4662a289d9d165b7d35c63e5405f0
|
4
|
+
data.tar.gz: 123197024b596d6a549e7b1b72f9b900b1edf1d26579299648d468c858f6fb1b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e34d2f9c2fcf1a487f7f9c4e4630997e015c3253c470e00b1dfddc14687f657eba7293c2a3c065002a96f7377828173aa567feede0d45ce59cba2da4a1cbcf2b
|
7
|
+
data.tar.gz: f28a0c77579fb25a9501a2a7cd8a97225440f93bcb1020d3291828348f8f9865b8f6de8e0d93341da6589d19f2a9a8061cef751428d28db4df6234f791464195
|
data/README.md
CHANGED
@@ -25,21 +25,12 @@ Here's an example of how to use the Fixed gem:
|
|
25
25
|
```ruby
|
26
26
|
require 'fixed'
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
b = Fixed(6)
|
28
|
+
a = Fixed(5)
|
29
|
+
b = Fixed(3)
|
31
30
|
|
32
|
-
# Perform arithmetic operations
|
33
|
-
sum = a + b
|
34
|
-
difference = a - b
|
35
|
-
product = a * b
|
36
31
|
ratio = a / b
|
37
32
|
|
38
|
-
#
|
39
|
-
puts "Sum: #{sum}" # => Sum: 7.00000000
|
40
|
-
puts "Difference: #{difference}" # => Difference: -5.00000000
|
41
|
-
puts "Product: #{product}" # => Product: 6.00000000
|
42
|
-
puts "Ratio: #{ratio.format(18)}" # => Ratio: 0.166666666666666667
|
33
|
+
puts ratio.format(18) # => prints 1.666666666666666667
|
43
34
|
```
|
44
35
|
|
45
36
|
In the above example, the Fixed gem allows you to perform arithmetic operations with high precision, avoiding rounding errors that can occur with floating-point numbers.
|
data/lib/fixed/version.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Fixed
|
4
|
-
VERSION = "1.
|
4
|
+
VERSION = "1.1.0"
|
5
5
|
end
|
6
6
|
|
7
7
|
|
@@ -11,6 +11,12 @@ __END__
|
|
11
11
|
# Minor version bump when backward-compatible changes or enhancements
|
12
12
|
# Patch version bump when backward-compatible bug fixes, security updates etc
|
13
13
|
|
14
|
+
1.1.0
|
15
|
+
|
16
|
+
- Support Ruby versions as old as 1.9.3
|
17
|
+
- Add #split for dividing a number proportionally based on specified ratios
|
18
|
+
- Add #pretty_format for printing with thousands separators
|
19
|
+
|
14
20
|
1.0.0
|
15
21
|
|
16
22
|
- Added Fixed-number with 18-digit precision
|
data/lib/fixed.rb
CHANGED
@@ -89,6 +89,31 @@ class Fixed
|
|
89
89
|
make(self.fractions.abs)
|
90
90
|
end
|
91
91
|
|
92
|
+
def split(*numbers)
|
93
|
+
ratios = numbers.map(&:to_fixed)
|
94
|
+
raise ArgumentError if ratios.any?(&:negative?)
|
95
|
+
raise ArgumentError if ratios.all?(&:zero?)
|
96
|
+
|
97
|
+
# This method ensures that the calculated portions add up to the original
|
98
|
+
# value and that no eps are lost due to rounding errors or other issues.
|
99
|
+
|
100
|
+
remainder = ratios.reduce(:+)
|
101
|
+
number = self
|
102
|
+
|
103
|
+
ratios.map do |ratio|
|
104
|
+
next Fixed.zero if remainder.zero?
|
105
|
+
|
106
|
+
part = make(division_with_rounding(
|
107
|
+
number.fractions * ratio.fractions,
|
108
|
+
remainder.fractions,
|
109
|
+
))
|
110
|
+
remainder -= ratio
|
111
|
+
number -= part
|
112
|
+
part
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
|
92
117
|
# ------- comparing -----------------------------------------------
|
93
118
|
|
94
119
|
def <=>(number)
|
@@ -101,15 +126,15 @@ class Fixed
|
|
101
126
|
end
|
102
127
|
|
103
128
|
def negative?
|
104
|
-
|
129
|
+
@fractions < 0
|
105
130
|
end
|
106
131
|
|
107
132
|
def positive?
|
108
|
-
|
133
|
+
@fractions > 0
|
109
134
|
end
|
110
135
|
|
111
136
|
def zero?
|
112
|
-
|
137
|
+
@fractions == 0
|
113
138
|
end
|
114
139
|
|
115
140
|
|
@@ -130,13 +155,23 @@ class Fixed
|
|
130
155
|
def format(precision = 8)
|
131
156
|
raise "expected 1..18, got #{precision.inspect}" unless (0..18) === precision
|
132
157
|
|
133
|
-
|
134
|
-
|
158
|
+
rounded_fractions = division_with_rounding(@fractions, 10 ** (18 - precision))
|
159
|
+
str = rounded_fractions.abs.to_s.rjust(precision + 1, ?0)
|
160
|
+
str.insert(-1 - precision, ?.) if precision > 0
|
161
|
+
"#{?- if @fractions < 0}#{str}#{?* if @fractions != 0 && str =~ /^[-0\.]*$/}"
|
162
|
+
end
|
163
|
+
|
164
|
+
def pretty_format(precision = 8)
|
165
|
+
str = format(precision).dup
|
166
|
+
stop = negative? ? 4 : 3
|
135
167
|
|
136
|
-
|
137
|
-
|
168
|
+
n = str.index('.') || str.length
|
169
|
+
str.insert(n -= 3, ',') while n > stop
|
170
|
+
|
171
|
+
return str
|
138
172
|
end
|
139
173
|
|
174
|
+
|
140
175
|
# ------- serialization -------------------------------------------
|
141
176
|
|
142
177
|
def to_json(state)
|
@@ -170,30 +205,36 @@ class Fixed
|
|
170
205
|
# variables, and we found caching the special variables to be faster
|
171
206
|
# (apparently new substrings are created upon each access), and also
|
172
207
|
# we found string concatenation and converting integer only once to
|
173
|
-
# be faster than two conversions and arithmetic
|
174
|
-
# correctly handles the sign for all negative numbers...
|
208
|
+
# be faster than two conversions and arithmetic operations.
|
175
209
|
|
176
210
|
str =~ /\A(-?\d+)(?:\.(\d{1,18}))?\Z/
|
177
211
|
whole, decimals = $1, $2
|
178
|
-
raise "expected
|
179
|
-
|
212
|
+
raise "expected valid string representation, got #{str.inspect}" unless whole
|
213
|
+
|
214
|
+
if decimals == nil
|
215
|
+
whole.to_i * 1000000000000000000
|
216
|
+
elsif decimals.length == 18
|
180
217
|
"#{whole}#{decimals}".to_i
|
181
|
-
elsif decimals
|
182
|
-
"#{whole}#{decimals}".to_i * (10 ** (18 - decimals.length))
|
183
218
|
else
|
184
|
-
whole.
|
219
|
+
"#{whole}#{decimals}".ljust(whole.length + 18, ?0).to_i
|
185
220
|
end
|
186
221
|
end
|
187
222
|
|
188
223
|
def self.number_as_fractions(number)
|
189
224
|
case number
|
190
225
|
when Float
|
191
|
-
#
|
192
|
-
#
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
226
|
+
# NOTE: Ensures consistency with the visible representation of floats
|
227
|
+
# to avoid rounding errors such as 16.50479841 => 1.6504798409999998976
|
228
|
+
|
229
|
+
number.to_s =~ /\A(-?\d+)(?:\.(\d+))(?:e([+-]\d+))?\Z/
|
230
|
+
whole, decimals, padding = $1, $2, $3.to_i + 18
|
231
|
+
raise "unsupported floating-point value: #{number}" unless whole
|
232
|
+
|
233
|
+
if padding < decimals.length
|
234
|
+
"#{whole}#{decimals}"[0..(padding - decimals.length - 1)].to_i
|
235
|
+
else
|
236
|
+
"#{whole}#{decimals}".ljust(whole.length + padding, ?0).to_i
|
237
|
+
end
|
197
238
|
else
|
198
239
|
Integer number * 1000000000000000000
|
199
240
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fixed
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adrian Kuhn
|
@@ -35,7 +35,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
35
35
|
requirements:
|
36
36
|
- - ">="
|
37
37
|
- !ruby/object:Gem::Version
|
38
|
-
version:
|
38
|
+
version: 1.9.3
|
39
39
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
40
40
|
requirements:
|
41
41
|
- - ">="
|