fixed 1.0.0 → 1.1.0
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 -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
|
- - ">="
|