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.
Files changed (5) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -12
  3. data/lib/fixed/version.rb +7 -1
  4. data/lib/fixed.rb +61 -20
  5. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0e87ed315b7c15cb15aa8663e00e8090b29d352babf0d30191da6d16ccca1640
4
- data.tar.gz: fb6001610413384c17bdd211bcbccd471c8e5c506e4c07bdabef3814903e11b3
3
+ metadata.gz: 61a3482844d4fbd66a1edb0dee9b686ba6a4662a289d9d165b7d35c63e5405f0
4
+ data.tar.gz: 123197024b596d6a549e7b1b72f9b900b1edf1d26579299648d468c858f6fb1b
5
5
  SHA512:
6
- metadata.gz: 333bb7867bddec4a1939e205d35e5c05e0443dce411f766d263f3d27c573a54c93a8d021204fbc4edf0d4b0c255a99b6323332464dfbe7afc14a2fbe1a6cf6e9
7
- data.tar.gz: edcda27654f5edce4d586d463d4f682e0f552a525eef203e68655efa751fbc2235437d34215332530e7cba0179420e366efa5146ef315deff24c6e53b687ee27
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
- # Create Fixed instances
29
- a = Fixed(1)
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
- # Output the results
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.0.0"
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
- self.fractions.negative?
129
+ @fractions < 0
105
130
  end
106
131
 
107
132
  def positive?
108
- self.fractions.positive?
133
+ @fractions > 0
109
134
  end
110
135
 
111
136
  def zero?
112
- self.fractions.zero?
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
- # Note: although Ruby's documentation indicates otherwise, Rational values
134
- # are actually formatted with full precision rather than as floats.
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
- str = "%.0#{precision}f" % Rational(@fractions, 1000000000000000000)
137
- (str =~ /^-?0(\.0+)?$/ && @fractions != 0) ? str << ?* : str
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 concatenation that
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 number with up to 18 decimal places, got #{str.inspect}" unless whole
179
- if decimals and decimals.length == 18
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.to_i * 1000000000000000000
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
- # This approach ensures consistency with the visible representation
192
- # of floats by avoiding rounding errors that may occur if we simply
193
- # multiply by 18 digits, considering that floats have only about
194
- # 15 digits of precision (see unit tests for examples).
195
-
196
- Integer Rational(number.to_s) * 1000000000000000000
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.0.0
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: 2.6.0
38
+ version: 1.9.3
39
39
  required_rubygems_version: !ruby/object:Gem::Requirement
40
40
  requirements:
41
41
  - - ">="