flt 1.5.0 → 1.5.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/History.txt +8 -0
- data/README.md +765 -0
- data/expand.rb +2 -0
- data/flt.gemspec +1 -0
- data/lib/flt.rb +1 -1
- data/lib/flt/bigdecimal.rb +2 -5
- data/lib/flt/bin_num.rb +1 -4
- data/lib/flt/complex.rb +10 -9
- data/lib/flt/dec_num.rb +9 -10
- data/lib/flt/float.rb +4 -6
- data/lib/flt/math.rb +3 -4
- data/lib/flt/num.rb +54 -21
- data/lib/flt/sugar.rb +1 -2
- data/lib/flt/support.rb +5 -2
- data/lib/flt/support/flag_values.rb +1 -1
- data/lib/flt/support/rationalizer.rb +3 -3
- data/lib/flt/support/rationalizer_extra.rb +6 -6
- data/lib/flt/support/reader.rb +1 -1
- data/lib/flt/tolerance.rb +2 -7
- data/lib/flt/trigonometry.rb +0 -2
- data/lib/flt/version.rb +1 -1
- data/setup.rb +1 -2
- data/test/generate_trig_data.rb +6 -6
- data/test/helper.rb +11 -5
- data/test/reader.rb +1 -1
- data/test/test_base_digits.rb +4 -2
- data/test/test_basic.rb +7 -7
- data/test/test_big_decimal.rb +21 -21
- data/test/test_bin.rb +1 -1
- data/test/test_bin_arithmetic.rb +1 -1
- data/test/test_binfloat_conversion.rb +1 -1
- data/test/test_coercion.rb +1 -1
- data/test/test_comparisons.rb +4 -4
- data/test/test_dectest.rb +2 -2
- data/test/test_define_conversions.rb +10 -10
- data/test/test_epsilon.rb +1 -1
- data/test/test_exact.rb +12 -11
- data/test/test_flags.rb +1 -1
- data/test/test_float.rb +23 -25
- data/test/test_format.rb +1 -1
- data/test/test_formatter.rb +6 -8
- data/test/test_hex_format.rb +2 -2
- data/test/test_multithreading.rb +1 -1
- data/test/test_normalized.rb +1 -1
- data/test/test_num_constructor.rb +6 -1
- data/test/test_odd_even.rb +1 -1
- data/test/test_rationalizer.rb +1 -1
- data/test/test_round.rb +36 -1
- data/test/test_sugar.rb +6 -6
- data/test/test_to_int.rb +4 -4
- data/test/test_to_rf.rb +126 -1
- data/test/test_tol.rb +1 -1
- data/test/test_trig.rb +1 -1
- data/test/test_ulp.rb +3 -3
- metadata +18 -4
- data/README.rdoc +0 -614
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4944139b67630d8b78c7f29d004a71b79003e757
|
4
|
+
data.tar.gz: '0290ba88b15d6b1b0d050fe62cbd4cdaeb4cc856'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c7c630691b5ca086da656641ca04a7ef3f163c16661bb2be6cd32785980c5facbf3fdf2779bce955f72e6352ad43d95435497b74428f0abe30eb745b6559e364
|
7
|
+
data.tar.gz: 071b9592aec3c4edc7994f922722c7cd81cfd96d06430569432df8de135500c459842eba8d98e20705120ed913aabe5f3ef69a02e56ac43c6320be90f68cfd71
|
data/History.txt
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
== 1.5.1 2017-02-28
|
2
|
+
|
3
|
+
* Bugfix & optimizations
|
4
|
+
- Num#to_f, BinNum#to_f are more accurate now
|
5
|
+
- Optimized Num#digits using Ruby 2.4 Integer#digits
|
6
|
+
- Optimized number_of_digits using Ruby 2.1 Integer#bit_length
|
7
|
+
- round interface is compatible with Ruby 2.4 options
|
8
|
+
|
1
9
|
== 1.5.0 2015-04-06
|
2
10
|
|
3
11
|
* New feature
|
data/README.md
ADDED
@@ -0,0 +1,765 @@
|
|
1
|
+
# Flt
|
2
|
+
|
3
|
+
[](http://badge.fury.io/rb/flt)
|
4
|
+
[](https://travis-ci.org/jgoizueta/flt)
|
5
|
+
|
6
|
+
This library provides arbitrary precision floating-point types for Ruby. All
|
7
|
+
types and functions are within a namespace called Flt. Decimal and Binary
|
8
|
+
floating point numbers are implemented in classes Flt::DecNum and Flt::BinNum.
|
9
|
+
These types are completely written in Ruby using the multiple precision native
|
10
|
+
integers. The performance could be improved in the future by using a C
|
11
|
+
extension based on the decNumber library.
|
12
|
+
|
13
|
+
The `Flt::Tolerance` classes and the `Flt.Tolerance()` constructor handle floating
|
14
|
+
point tolerances defined in flexible ways.
|
15
|
+
|
16
|
+
Context classes are defined in the files [flt/float.rb](lib/flt/float_rb.html)
|
17
|
+
and [flt/bigdecimal.rb](lib/flt/bigdecimal_rb.html) for Float and BigDecimal
|
18
|
+
numbers that aid to the interchangeability of floating point types. This
|
19
|
+
represent the only definition of identifiers outside the Flt namespace: the
|
20
|
+
methods Float.context() and BigDecimal.context() and some contants in Float.
|
21
|
+
|
22
|
+
This library is the successor of the ruby-decimal gem, that defined the
|
23
|
+
Decimal class for decimal floating point; that class has been renamed to
|
24
|
+
`Flt::DecNum` and support has been added for binary floating point and
|
25
|
+
tolerances.
|
26
|
+
|
27
|
+
The documentation for this package is available at
|
28
|
+
http://www.rubydoc.info/github/jgoizueta/flt/master
|
29
|
+
|
30
|
+
The code is at http://github.com/jgoizueta/flt/
|
31
|
+
|
32
|
+
## DecNum
|
33
|
+
|
34
|
+
`Flt::DecNum` is a standards-compliant arbitrary precision decimal
|
35
|
+
floating-point type for Ruby. It is based on the Python Decimal class.
|
36
|
+
|
37
|
+
## Standars compliance.
|
38
|
+
|
39
|
+
DecNum is designed to be conformant to the General Decimal Arithmetic
|
40
|
+
Specification and the revised IEEE 754 standard (IEEE 754-2008).
|
41
|
+
|
42
|
+
# Examples of use
|
43
|
+
|
44
|
+
To install the library use gem from the command line:
|
45
|
+
|
46
|
+
gem install flt
|
47
|
+
|
48
|
+
Then require the library in your code (if it fails you may need to `require
|
49
|
+
'rubygems'` first)
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
require 'flt'
|
53
|
+
include Flt
|
54
|
+
```
|
55
|
+
|
56
|
+
Now we can use the DecNum class simply like this:
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
puts DecNum(1)/DecNum(3) # -> 0.3333333333333333333333333333
|
60
|
+
```
|
61
|
+
|
62
|
+
DecNum() is a constructor that can be used instead of DecNum.new()
|
63
|
+
|
64
|
+
## Contexts
|
65
|
+
|
66
|
+
Contexts are environments for arithmetic operations. They govern precision,
|
67
|
+
set rules for rounding, determine which signals are treated as exceptions, and
|
68
|
+
limit the range for exponents.
|
69
|
+
|
70
|
+
Each thread has an active context that can be accessed like this:
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
puts DecNum.context.precision # -> 28
|
74
|
+
```
|
75
|
+
|
76
|
+
The active context can be modified globally for the current thread:
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
DecNum.context.precision = 2
|
80
|
+
puts DecNum.context.precision # -> 2
|
81
|
+
puts DecNum(1)/DecNum(3) # -> 0.33
|
82
|
+
DecNum.context.precision += 7
|
83
|
+
puts DecNum.context.precision # -> 9
|
84
|
+
puts DecNum(1)/DecNum(3) # -> 0.333333333
|
85
|
+
```
|
86
|
+
|
87
|
+
Or it can be altered locally inside a block:
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
DecNum.context do
|
91
|
+
DecNum.context.precision = 5
|
92
|
+
puts DecNum.context.precision # -> 5
|
93
|
+
end
|
94
|
+
puts DecNum.context.precision # -> 9
|
95
|
+
```
|
96
|
+
|
97
|
+
The block for a local context can be passed the current context as an
|
98
|
+
argument:
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
DecNum.context do |local_context|
|
102
|
+
local_context.precision = 5
|
103
|
+
puts DecNum.context.precision # -> 5
|
104
|
+
end
|
105
|
+
puts DecNum.context.precision # -> 9
|
106
|
+
```
|
107
|
+
|
108
|
+
A context object can also be used to define the local context:
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
my_context = DecNum::Context(precision: 20)
|
112
|
+
DecNum.context(my_context) do |context|
|
113
|
+
puts context.precision
|
114
|
+
end # -> 20
|
115
|
+
```
|
116
|
+
|
117
|
+
And individual parameters can be assigned like this:
|
118
|
+
|
119
|
+
```ruby
|
120
|
+
puts DecNum.context.precision # -> 9
|
121
|
+
puts DecNum.context.rounding # -> half_even
|
122
|
+
DecNum.context(rounding: :down) do |context|
|
123
|
+
puts context.precision # -> 9
|
124
|
+
puts context.rounding # -> down
|
125
|
+
end
|
126
|
+
```
|
127
|
+
|
128
|
+
Contexts created with the `DecNum::Context()` constructor inherit from
|
129
|
+
DecNum::DefaultContext. Default context attributes can be established by
|
130
|
+
modifying that object:
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
DecNum::DefaultContext.precision = 10
|
134
|
+
DecNum.context = DecNum::Context(rounding: :half_up)
|
135
|
+
puts DecNum.context.precision # -> 10
|
136
|
+
```
|
137
|
+
|
138
|
+
Note that a context object assigned to DecNum.context is copied, so it is not
|
139
|
+
altered through DecNum.context:
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
puts my_context.precision # -> 20
|
143
|
+
DecNum.context = my_context
|
144
|
+
DecNum.context.precision = 2
|
145
|
+
puts my_context.precision # -> 20
|
146
|
+
```
|
147
|
+
|
148
|
+
So, DefaultContext is not altered when modifying DecNum.context.
|
149
|
+
|
150
|
+
Methods that use a context have an optional parameter to override the active
|
151
|
+
context (`DecNum.context`) :
|
152
|
+
|
153
|
+
```ruby
|
154
|
+
DecNum.context.precision = 3
|
155
|
+
puts DecNum(1).divide(3) # -> 0.333
|
156
|
+
puts DecNum(1).divide(3, my_context) # -> 0.33333333333333333333
|
157
|
+
```
|
158
|
+
|
159
|
+
Individual context parameters can also be overriden:
|
160
|
+
|
161
|
+
```ruby
|
162
|
+
puts DecNum(1).divide(3, precision: 6) # -> 0.333333
|
163
|
+
```
|
164
|
+
|
165
|
+
There are two additional predefined contexts `DecNum::ExtendedContext` and
|
166
|
+
DecNum::BasicContext that are not meant to be modified; they can be used to
|
167
|
+
achieve reproducible results. We will use `DecNum::ExtendedContext` in the
|
168
|
+
following examples:
|
169
|
+
|
170
|
+
```ruby
|
171
|
+
DecNum.context = DecNum::ExtendedContext
|
172
|
+
```
|
173
|
+
|
174
|
+
Most decimal operations can be executed by using either Context or DecNum
|
175
|
+
methods:
|
176
|
+
|
177
|
+
```ruby
|
178
|
+
puts DecNum.context.exp(1) # -> 2.71828183
|
179
|
+
puts DecNum(1).exp # -> 2.71828183
|
180
|
+
```
|
181
|
+
|
182
|
+
If using Context methods, values are automatically converted as if the
|
183
|
+
DecNum() constructor was used.
|
184
|
+
|
185
|
+
## Rounding
|
186
|
+
|
187
|
+
Results are normally rounded using the precision (number of significant
|
188
|
+
digits) and rounding mode defined in the context.
|
189
|
+
|
190
|
+
```ruby
|
191
|
+
DecNum.context.precision = 4
|
192
|
+
puts DecNum(1)/DecNum(3) # -> 0.3333
|
193
|
+
puts DecNum('1E20')-DecNum('1E-20') # -> 1.000E+20
|
194
|
+
DecNum.context.rounding = :half_up
|
195
|
+
puts +DecNum('100.05') # -> 100.1
|
196
|
+
DecNum.context.rounding = :half_even
|
197
|
+
puts +DecNum('100.05') # -> 100.0
|
198
|
+
```
|
199
|
+
|
200
|
+
Note that input values are not rounded, only results; we use the plus operator
|
201
|
+
to force rounding here:
|
202
|
+
|
203
|
+
```ruby
|
204
|
+
DecNum.context.precision = 4
|
205
|
+
x = DecNum('123.45678')
|
206
|
+
puts x # -> 123.45678
|
207
|
+
puts +x # -> 123.5
|
208
|
+
```
|
209
|
+
|
210
|
+
Precision can be also set to 'exact' to avoid rounding, by using the exact
|
211
|
+
property or using a 0 precision. In exact mode results are never rounded and
|
212
|
+
results that have an infinite number of digits trigger the `DecNum::Inexact`
|
213
|
+
exception.
|
214
|
+
|
215
|
+
```ruby
|
216
|
+
DecNum.context.exact = true
|
217
|
+
puts DecNum('1E20')-DecNum('1E-20') # -> 99999999999999999999.99999999999999999999
|
218
|
+
puts DecNum(16).sqrt # -> 4
|
219
|
+
puts DecNum(16)/DecNum(4) # -> 4
|
220
|
+
puts DecNum(1)/DecNum(3) # -> Exception : Flt::Num::Inexact
|
221
|
+
|
222
|
+
DecNum.context.precision = 5
|
223
|
+
puts DecNum('1E20')-DecNum('1E-20') # -> 1.0000E+20
|
224
|
+
puts DecNum(16).sqrt # -> 4
|
225
|
+
puts DecNum(16)/DecNum(4) # -> 4
|
226
|
+
puts DecNum(1)/DecNum(3) # -> 0.33333
|
227
|
+
```
|
228
|
+
|
229
|
+
There are also some methods for explicit rounding that provide an interface
|
230
|
+
compatible with that of the Ruby `Float` class:
|
231
|
+
|
232
|
+
```ruby
|
233
|
+
puts DecNum('101.5').round # -> 102
|
234
|
+
puts DecNum('101.5').round(0) # -> 102
|
235
|
+
puts DecNum('101.12345').round(2) # -> 101.12
|
236
|
+
puts DecNum('101.12345').round(-1) # -> 1.0E+2
|
237
|
+
puts DecNum('101.12345').round(places: 2) # -> 101.12
|
238
|
+
puts DecNum('101.12345').round(precision: 2) # -> 1.0E+2
|
239
|
+
puts DecNum('101.5').round(rounding: :half_up) # -> 102
|
240
|
+
puts DecNum('101.5').ceil # -> 102
|
241
|
+
puts DecNum('101.5').floor # -> 101
|
242
|
+
puts DecNum('101.5').truncate # -> 101
|
243
|
+
```
|
244
|
+
|
245
|
+
## Special values
|
246
|
+
|
247
|
+
In addition to finite numbers, a DecNum object can represent some special
|
248
|
+
values:
|
249
|
+
* Infinity (+Infinity, -Infinity). The method DecNum#infinite? returns true
|
250
|
+
for these to values. DecNum.infinity DecNum.infinity(-1) can be used to
|
251
|
+
get these values.
|
252
|
+
* NaN (not a number) represents undefined results. The method DecNum#nan?
|
253
|
+
returns true for it and DecNum.nan can be used to obtain it. There is a
|
254
|
+
variant, sNaN (signaling NaN) that causes an invalid operation condition
|
255
|
+
if used; it can be detected with DecNum.snan?. A NaN can also include
|
256
|
+
diagnostic information in its sign and coefficient.
|
257
|
+
|
258
|
+
Any of the special values can be detected with DecNum#special? Finite numbers
|
259
|
+
can be clasified with these methods:
|
260
|
+
* DecNum#zero? detects a zero value (note that there are two zero values: +0
|
261
|
+
and -0)
|
262
|
+
* DecNum#normal? detects normal values: those whose adjusted exponents are
|
263
|
+
not less than @emin@.
|
264
|
+
* DecNum#subnormal? detects subnormal values: those whose adjusted exponents
|
265
|
+
are less than @emin@.
|
266
|
+
|
267
|
+
## Exceptions
|
268
|
+
|
269
|
+
Exceptional conditions that may arise during operations have corresponding
|
270
|
+
classes that represent them:
|
271
|
+
* DecNum::InvalidOperation
|
272
|
+
* DecNum::DivisionByZero
|
273
|
+
* DecNum::DivisionImpossible
|
274
|
+
* DecNum::DivisionUndefined
|
275
|
+
* DecNum::Inexact
|
276
|
+
* DecNum::Overflow
|
277
|
+
* DecNum::Underflow
|
278
|
+
* DecNum::Clamped
|
279
|
+
* DecNum::InvalidContext
|
280
|
+
* DecNum::Rounded
|
281
|
+
* DecNum::Subnormal
|
282
|
+
* DecNum::ConversionSyntax
|
283
|
+
|
284
|
+
For each condition, a flag and a trap (boolean values) exist in the context.
|
285
|
+
When a condition occurs, the corresponding flag in the context takes the value
|
286
|
+
true (and remains set until cleared) and a exception is raised if the
|
287
|
+
corresponding trap has the value true.
|
288
|
+
|
289
|
+
```ruby
|
290
|
+
DecNum.context.traps[DecNum::DivisionByZero] = false
|
291
|
+
DecNum.context.flags[DecNum::DivisionByZero] = false
|
292
|
+
puts DecNum(1)/DecNum(0) # -> Infinity
|
293
|
+
puts DecNum.context.flags[DecNum::DivisionByZero] # -> true
|
294
|
+
DecNum.context.traps[DecNum::DivisionByZero] = true
|
295
|
+
puts DecNum(1)/DecNum(0) # -> Exception : Flt::Num::DivisionByZero
|
296
|
+
```
|
297
|
+
|
298
|
+
## Numerical conversion
|
299
|
+
|
300
|
+
By default, `DecNum` is interoperable with `Integer` and `Rational`. Conversion
|
301
|
+
happens automatically to operands:
|
302
|
+
|
303
|
+
```ruby
|
304
|
+
puts DecNum('0.1') + 1 # -> 1.1
|
305
|
+
puts 7 + DecNum('0.2') # -> 7.2
|
306
|
+
puts Rational(5,2) + DecNum('3') # -> 5.5
|
307
|
+
```
|
308
|
+
|
309
|
+
Conversion can also be done explicitly with the DecNum constructor:
|
310
|
+
|
311
|
+
```ruby
|
312
|
+
puts DecNum(7) # -> 7
|
313
|
+
puts DecNum(Rational(1,10)) # -> 0.1
|
314
|
+
```
|
315
|
+
|
316
|
+
Converting a `DecNum` to other numerical types can be done with specific
|
317
|
+
Ruby-style methods. (note the truncated result of `to_i`)
|
318
|
+
|
319
|
+
```ruby
|
320
|
+
puts DecNum('1.1').to_i # -> 1
|
321
|
+
puts DecNum('1.1').to_r # -> 11/10
|
322
|
+
```
|
323
|
+
|
324
|
+
Or with a generic method:
|
325
|
+
|
326
|
+
```ruby
|
327
|
+
puts DecNum('1.1').convert_to(Integer) # -> 1
|
328
|
+
puts DecNum('1.1').convert_to(Rational) # -> 11/10
|
329
|
+
```
|
330
|
+
|
331
|
+
Thera are also GDAS style conversion operations:
|
332
|
+
|
333
|
+
```ruby
|
334
|
+
puts DecNum('1.1').to_integral_value # -> 1
|
335
|
+
```
|
336
|
+
|
337
|
+
And conversion is also possible to `Float`:
|
338
|
+
|
339
|
+
```ruby
|
340
|
+
puts DecNum('1.1').to_f # -> 1.1
|
341
|
+
puts DecNum('1.1').convert_to(Float) # -> 1.1
|
342
|
+
puts Float(DecNum('1.1')) # -> 1.1
|
343
|
+
```
|
344
|
+
|
345
|
+
Types with predefined bidirectional conversion (`Integer` and `Rational`) can be
|
346
|
+
operated with `DecNum` on either side of an operator, and the result will be a
|
347
|
+
`DecNum`. For `Float` there is no predefined bidirectional conversion (see below
|
348
|
+
how to define it) and the result of an operation between `DecNum` and `Float` will
|
349
|
+
be of type `Float`.
|
350
|
+
|
351
|
+
```ruby
|
352
|
+
puts (DecNum('1.1') + 2.0).class # -> Float
|
353
|
+
puts (2.0 + DecNum('1.1')).class # -> Float
|
354
|
+
```
|
355
|
+
|
356
|
+
The conversion system is extensible. For example, we can include BigDecimal
|
357
|
+
into it by defining suitable conversion procedures:
|
358
|
+
|
359
|
+
```ruby
|
360
|
+
DecNum.context.define_conversion_from(BigDecimal) do |x, context|
|
361
|
+
DecNum(x.to_s)
|
362
|
+
end
|
363
|
+
DecNum.context.define_conversion_to(BigDecimal) do |x|
|
364
|
+
BigDecimal.new(x.to_s)
|
365
|
+
end
|
366
|
+
```
|
367
|
+
|
368
|
+
Now we can mix `BigDecimals` and `Decimals` in expressions and convert from `DecNum`
|
369
|
+
to `BigDecimal`:
|
370
|
+
|
371
|
+
```ruby
|
372
|
+
puts BigDecimal.new('1.1') + DecNum('2.2') # -> 3.3
|
373
|
+
puts DecNum('1.1').convert_to(BigDecimal) # -> 0.11E1
|
374
|
+
```
|
375
|
+
|
376
|
+
Note that the conversions are defined in a `Context` object and will be
|
377
|
+
available only when that context applies. That way we can define conversions
|
378
|
+
for specific purposes without affecting a program globally.
|
379
|
+
|
380
|
+
As another example consider conversion from Float to DecNum, which is not
|
381
|
+
defined by default because it can be defined in different ways depending on
|
382
|
+
the purpose.
|
383
|
+
|
384
|
+
A `Float` constant such as 0.1 defines a `Float` object which has a numerical
|
385
|
+
value close to, but not exactly 1/10. When converting that `Float` to `DecNum` we
|
386
|
+
could decide to preserve the exact numerical value of the number or try to
|
387
|
+
find a simple decimal expression within a given tolerance. If we take the
|
388
|
+
first approach we can define this conversion:
|
389
|
+
|
390
|
+
```ruby
|
391
|
+
DecNum.context.define_conversion_from(Float) do |x, context|
|
392
|
+
s,e = Math.frexp(x)
|
393
|
+
s = Math.ldexp(s, Float::MANT_DIG).to_i
|
394
|
+
e -= Float::MANT_DIG
|
395
|
+
DecNum(s*(Float::RADIX**e))
|
396
|
+
end
|
397
|
+
```
|
398
|
+
|
399
|
+
Note that the conversion we've defined depends on the context precision:
|
400
|
+
|
401
|
+
```ruby
|
402
|
+
DecNum.local_context(precision: 20) { puts DecNum(0.1) } # -> 0.10000000000000000555
|
403
|
+
DecNum.local_context(precision: 12) { puts DecNum(0.1) } # -> 0.100000000000
|
404
|
+
DecNum.local_context(exact: true) { puts DecNum(0.1) } # -> 0.1000000000000000055511151231257827021181583404541015625
|
405
|
+
```
|
406
|
+
|
407
|
+
A different approach for Float to DecNum conversion is to find the shortest
|
408
|
+
(fewer digits) DecNum that rounds to the Float with the binary precision that
|
409
|
+
the Float has. We will assume that the DecNum to Float conversion done with
|
410
|
+
the rounding mode of the DecNum context. The BinNum class has a method to
|
411
|
+
perform this kind of conversion, so we will use it.
|
412
|
+
|
413
|
+
```ruby
|
414
|
+
DecNum.context.define_conversion_from(Float) do |x, dec_context|
|
415
|
+
BinNum.context(:rounding=>dec_context.rounding) do |bin_context|
|
416
|
+
BinNum(x).to_decimal
|
417
|
+
end
|
418
|
+
end
|
419
|
+
```
|
420
|
+
|
421
|
+
The result is independent of the context precision.
|
422
|
+
|
423
|
+
```ruby
|
424
|
+
puts DecNum(0.1) # -> 0.1
|
425
|
+
puts DecNum(1.0/3) # -> 0.3333333333333333
|
426
|
+
```
|
427
|
+
|
428
|
+
This conversion gives the results expected most of the time, but it must be
|
429
|
+
noticed that there must be some compromise, because different decimal literals
|
430
|
+
convert to the same `Float` value:
|
431
|
+
|
432
|
+
```ruby
|
433
|
+
puts DecNum(0.10000000000000001) # -> 0.1
|
434
|
+
```
|
435
|
+
|
436
|
+
There's also some uncertainty because the way the Ruby interpreter parses
|
437
|
+
Float literals may not be well specified; in the usual case (IEEE Double
|
438
|
+
Floats and round-to-even) the results will be as expected (correctly rounded
|
439
|
+
Floats), but some platforms may behave differently.
|
440
|
+
|
441
|
+
The `BinNum` also a instance method `to_decimal_exact` to perform the previous
|
442
|
+
'exact' conversion, that could have be written:
|
443
|
+
|
444
|
+
```ruby
|
445
|
+
DecNum.context.define_conversion_from(Float) do |x, context|
|
446
|
+
DecNum.context(context) do
|
447
|
+
BinNum(x).to_decimal_exact
|
448
|
+
end
|
449
|
+
end
|
450
|
+
```
|
451
|
+
|
452
|
+
## Abbreviation
|
453
|
+
|
454
|
+
The use of DecNum can be made less verbose by requiring:
|
455
|
+
|
456
|
+
```ruby
|
457
|
+
require 'flt/d'
|
458
|
+
```
|
459
|
+
|
460
|
+
This file defines `D` as a synonym for `DecNum`:
|
461
|
+
|
462
|
+
```ruby
|
463
|
+
D.context.precision = 3
|
464
|
+
puts +D('1.234') # -> 1.23
|
465
|
+
```
|
466
|
+
|
467
|
+
Some convenient methods are added to numeric classes by requiring the optional
|
468
|
+
[flt/sugar.rb](lib/flt/sugar_rb.html). This must be explicitely required
|
469
|
+
because it could cause conflicts with other extensions of these classes.
|
470
|
+
|
471
|
+
```ruby
|
472
|
+
require 'flt/sugar'
|
473
|
+
|
474
|
+
puts 34.odd? # -> false
|
475
|
+
puts 34.even? # -> true
|
476
|
+
puts 0.1.split.inspect # -> [1, 7205759403792794, -56]
|
477
|
+
puts (-0.1).sign # -> -1
|
478
|
+
```
|
479
|
+
|
480
|
+
A shortcut notation for DecNum is defined in this file (based on an idea by
|
481
|
+
[coderrr](http://coderrr.wordpress.com) which allows exact definitions with
|
482
|
+
almost literal decimal syntax (note the underscore after the dot.)
|
483
|
+
|
484
|
+
```ruby
|
485
|
+
puts 10._123456789123456789 # -> 10.123456789123456789
|
486
|
+
```
|
487
|
+
|
488
|
+
Additional underscores can be used to separate digits at any place except
|
489
|
+
before the decimal point:
|
490
|
+
|
491
|
+
```ruby
|
492
|
+
puts 100_000._000_001 # -> 100000.000001
|
493
|
+
puts 100_000._000_001.class # -> Flt::DecNum
|
494
|
+
```
|
495
|
+
|
496
|
+
But note that `100_000.000_001` is a valid `Float` (it's not a `DecNum` because
|
497
|
+
there isn't an underscore just after the decimal point):
|
498
|
+
|
499
|
+
```ruby
|
500
|
+
puts 100_000.000_001 # -> 100000.000001
|
501
|
+
puts 100_000.000_001.class # -> Float
|
502
|
+
```
|
503
|
+
|
504
|
+
There's also one important caveat with this notation: negative numbers with a
|
505
|
+
zero integral part must be parenthesed (otherwise the minus has no effect
|
506
|
+
because it affects only the null integer part):
|
507
|
+
|
508
|
+
```ruby
|
509
|
+
puts -0._5 # -> 0.5
|
510
|
+
puts -(0._5) # -> -0.5
|
511
|
+
```
|
512
|
+
|
513
|
+
## Error analysis
|
514
|
+
|
515
|
+
The DecNum#ulp() method returns the value of a "unit in the last place" for a
|
516
|
+
given number under the current context.
|
517
|
+
|
518
|
+
```ruby
|
519
|
+
D.context.precision = 4
|
520
|
+
puts D('1.5').ulp # -> 0.001
|
521
|
+
puts D('1.5E10').ulp # -> 1E+7
|
522
|
+
```
|
523
|
+
|
524
|
+
Whe can compute the error in ulps of an approximation `aprx` to correclty
|
525
|
+
rounded value `exct` with:
|
526
|
+
|
527
|
+
```ruby
|
528
|
+
def ulps(exct, aprx)
|
529
|
+
(aprx-exct).abs/exct.ulp
|
530
|
+
end
|
531
|
+
|
532
|
+
puts ulps(DecNum('0.5000'), DecNum('0.5003')) # -> 3
|
533
|
+
puts ulps(DecNum('0.5000'), DecNum('0.4997')) # -> 3
|
534
|
+
|
535
|
+
puts ulps(DecNum('0.1000'), DecNum('0.1003')) # -> 3E+1
|
536
|
+
puts ulps(DecNum('0.1000'), DecNum('0.0997')) # -> 3E+1
|
537
|
+
|
538
|
+
puts ulps(DecNum(1), DecNum(10).next_minus) # -> 8.999E+4
|
539
|
+
puts ulps(DecNum(1), DecNum(10).next_plus) # -> 9.01E+4
|
540
|
+
```
|
541
|
+
|
542
|
+
Note that in the definition of ulps we use exct.ulp. If we had use aprx.ulp
|
543
|
+
DecNum(10).next_plus would seem to be a better approximation to DecNum(1) than
|
544
|
+
DecNum(10).next_minus. (Admittedly, such bad approximations should not be
|
545
|
+
common.)
|
546
|
+
|
547
|
+
## BinNum Input/Output
|
548
|
+
|
549
|
+
BinNum can be defined with a decimal string literal and converted to one with
|
550
|
+
to_s, as DecNum, but since this involves a change of base these are inexact
|
551
|
+
operations subject to some specific precision limits.
|
552
|
+
|
553
|
+
If we define the number with a binary literal, no base conversion is involved
|
554
|
+
and the result is exactly defined; here we define a number with just one bit
|
555
|
+
of precision:
|
556
|
+
|
557
|
+
```ruby
|
558
|
+
x = BinNum('0.001', base: 2)
|
559
|
+
puts x.number_of_digits # -> 1
|
560
|
+
puts x.to_s(base: 2) # -> 0.001
|
561
|
+
```
|
562
|
+
|
563
|
+
Note that we could have defined it with more precision, e.g.
|
564
|
+
|
565
|
+
```ruby
|
566
|
+
y = BinNum('0.001000', base: 2)
|
567
|
+
puts y.number_of_digits # -> 4
|
568
|
+
puts y.to_s(base: 2) # -> 0.001000
|
569
|
+
```
|
570
|
+
|
571
|
+
But let's get back to our one bit quantity, x, and convert it to a decimal
|
572
|
+
string. Since the internal precision is only one bit it contains very little
|
573
|
+
information:
|
574
|
+
|
575
|
+
```ruby
|
576
|
+
puts x # -> 0.1
|
577
|
+
```
|
578
|
+
|
579
|
+
We can obtain more digits with the :all_digits option which show all the
|
580
|
+
decimal digits that are significative for the given precision of 1 bit:
|
581
|
+
|
582
|
+
```ruby
|
583
|
+
puts x.to_s(all_digits: true) # -> 0.12
|
584
|
+
```
|
585
|
+
|
586
|
+
We can also obtain the exact value of x by using the Num.convert_exact method
|
587
|
+
to convert it to decimal (base 10):
|
588
|
+
|
589
|
+
```ruby
|
590
|
+
puts Num.convert_exact(x,10) # -> 0.125
|
591
|
+
```
|
592
|
+
|
593
|
+
Let's convert the default decimal output back to another BinNum which will
|
594
|
+
preserve its precision:
|
595
|
+
|
596
|
+
```ruby
|
597
|
+
y = BinNum(x.to_s)
|
598
|
+
```
|
599
|
+
|
600
|
+
The result may seem ok:
|
601
|
+
|
602
|
+
```ruby
|
603
|
+
puts y # -> 0.1
|
604
|
+
```
|
605
|
+
|
606
|
+
But is not exactly what we originally had:
|
607
|
+
|
608
|
+
```ruby
|
609
|
+
puts y==x # -> false
|
610
|
+
puts y # -> 0.1
|
611
|
+
puts y.number_of_digits # -> 5
|
612
|
+
puts y.to_s(base: 2) # -> 0.00011010
|
613
|
+
```
|
614
|
+
|
615
|
+
The new value y has gained some digits because of the intermediate conversion
|
616
|
+
to decimal: one decimal digit contains more information than one bit, and the
|
617
|
+
result shows that.
|
618
|
+
|
619
|
+
If we wanted to preserve exactly the number we should have done this:
|
620
|
+
|
621
|
+
```ruby
|
622
|
+
y = BinNum(x.to_s, :fixed, precision: x.number_of_digits)
|
623
|
+
puts y==x # -> true
|
624
|
+
```
|
625
|
+
|
626
|
+
To preserve the value we had to explicitly determine how many bits should y
|
627
|
+
have.
|
628
|
+
|
629
|
+
With the :fixed options the number produced by BinNum is rounded to the
|
630
|
+
context precision (which can be overriden as in the example by other options):
|
631
|
+
|
632
|
+
```ruby
|
633
|
+
puts BinNum(x.to_s, :fixed, precision: 32).to_s(base: 2) # -> 0.00011001100110011001100110011001101
|
634
|
+
puts BinNum(x.to_s, :fixed, precision: 1).to_s(base: 2) # -> 0.001
|
635
|
+
```
|
636
|
+
|
637
|
+
Note also that if we normalize a value we will change it's precision to that
|
638
|
+
of the context:
|
639
|
+
|
640
|
+
```ruby
|
641
|
+
puts x.number_of_digits # -> 1
|
642
|
+
puts x.normalize.number_of_digits # -> 53
|
643
|
+
puts x.normalize.to_s # -> 0.125
|
644
|
+
```
|
645
|
+
|
646
|
+
## Mathematical functions
|
647
|
+
|
648
|
+
There are two mathematical functions modules analogous to Ruby's Math for
|
649
|
+
Float, Flt::DecNum::Math and Flt::BinNum::Math. Currently they consist of
|
650
|
+
basic trigonometric functions, including hypot, logarithms and the exponential
|
651
|
+
function, and the constants e and pi.
|
652
|
+
|
653
|
+
Its functions can be accessed in a number of ways:
|
654
|
+
|
655
|
+
```ruby
|
656
|
+
require 'flt/math'
|
657
|
+
DecNum.context(precision: 10) do |context|
|
658
|
+
# As module functions, using the current context for the enclosing Num class:
|
659
|
+
puts DecNum::Math.sin(1)*DecNum::Math.pi # -> 2.643559064
|
660
|
+
# As functions of a context object:
|
661
|
+
puts context.sin(1)*context.pi # -> 2.643559064
|
662
|
+
# Through a math block:
|
663
|
+
puts DecNum.context.math{sin(1)*pi} # -> 2.643559064
|
664
|
+
puts DecNum.math{sin(1)*pi} # -> 2.643559064
|
665
|
+
# And can be *included* to be used as private instance methods:
|
666
|
+
include DecNum::Math
|
667
|
+
puts sin(1)*pi # -> 2.643559064
|
668
|
+
end
|
669
|
+
```
|
670
|
+
|
671
|
+
## More Information
|
672
|
+
|
673
|
+
* Decimal Floating point type: see the base Flt::Num class and the
|
674
|
+
Flt::DecNum class
|
675
|
+
* Binary Floating point type: see the base Flt::Num class and the
|
676
|
+
Flt::BinNum class
|
677
|
+
* Floating Point Contexts: see documentation for classes
|
678
|
+
Flt::Num::ContextBase, Flt::DecNum::Context and Flt::BinNum::Context
|
679
|
+
* Floating Point Tolerance: see the
|
680
|
+
[flt/tolerance.rb](lib/flt/tolerance_rb.html) file and the Flt::Tolerance
|
681
|
+
class
|
682
|
+
* Constructors: see Flt.DecNum(), Flt.BinNum() and Flt.Tolerance().
|
683
|
+
* Trigonometry functions: see Flt::Trigonometry.
|
684
|
+
* Complex number support: see the [flt/complex.rb](lib/flt/complex_rb.html)
|
685
|
+
file
|
686
|
+
|
687
|
+
# DecNum vs BigDecimal
|
688
|
+
|
689
|
+
DecNum solves some of the difficulties of using BigDecimal.
|
690
|
+
|
691
|
+
One of the major problems with BigDecimal is that it's not easy to control the
|
692
|
+
number of significant digits of the results. While addition, subtraction and
|
693
|
+
multiplication are exact (unless a limit is used), divisions will need to be
|
694
|
+
passed precision explicitly or else an indeterminate number of significant
|
695
|
+
digits will be lost. Part of the problem is that numbers don't keep track of
|
696
|
+
its precision (0.1000 is not distinguishable from 0.1.)
|
697
|
+
|
698
|
+
With DecNum, Context objects are used to specify the exact number of digits to
|
699
|
+
be used for all operations making the code cleaner and the results more easily
|
700
|
+
predictable.
|
701
|
+
|
702
|
+
```ruby
|
703
|
+
DecNum.context.precision = 10
|
704
|
+
puts DecNum(1)/DecNum(3)
|
705
|
+
```
|
706
|
+
|
707
|
+
Contexts are thread-safe and can be used for individual operations:
|
708
|
+
|
709
|
+
```ruby
|
710
|
+
puts DecNum(1).divide(DecNum(e), DecNum::Context(precision: 4))
|
711
|
+
```
|
712
|
+
|
713
|
+
Which can be abbreviated:
|
714
|
+
|
715
|
+
```ruby
|
716
|
+
puts DecNum(1).divide(DecNum(e), precision: 4)
|
717
|
+
```
|
718
|
+
|
719
|
+
Or use locally in a block without affecting other code:
|
720
|
+
|
721
|
+
```ruby
|
722
|
+
DecNum.context {
|
723
|
+
DecNum.context.precision = 3
|
724
|
+
puts DecNum(1)/DecNum(3)
|
725
|
+
}
|
726
|
+
puts DecNum.context.precision
|
727
|
+
```
|
728
|
+
|
729
|
+
Which can also be abbreviated:
|
730
|
+
|
731
|
+
```ruby
|
732
|
+
DecNum.context(precision: 3) { puts DecNum(1)/DecNum(3) }
|
733
|
+
```
|
734
|
+
|
735
|
+
This allows in general to write simpler code; e.g. this is an exponential
|
736
|
+
function, adapted from the 'recipes' in Python's Decimal:
|
737
|
+
|
738
|
+
```ruby
|
739
|
+
def exp(x, c=nil)
|
740
|
+
i, lasts, s, fact, num = 0, 0, 1, 1, 1
|
741
|
+
DecNum.context(c) do |context|
|
742
|
+
context.precision += 2
|
743
|
+
while s != lasts
|
744
|
+
lasts = s
|
745
|
+
i += 1
|
746
|
+
fact *= i
|
747
|
+
num *= x
|
748
|
+
s += num / fact
|
749
|
+
end
|
750
|
+
end
|
751
|
+
return +s
|
752
|
+
end
|
753
|
+
```
|
754
|
+
|
755
|
+
The final unary + applied to the result forces it to be rounded to the current
|
756
|
+
precision (because we have computed it with two extra digits) The result of
|
757
|
+
this method does not have trailing non-significant digits, as is common with
|
758
|
+
BigDecimal (e.g. in the exp implementation available in the standard Ruby
|
759
|
+
library, in bigdecimal/math)
|
760
|
+
|
761
|
+
# Roadmap
|
762
|
+
|
763
|
+
* Trigonometry optimizations
|
764
|
+
* Implement the missing GDA functions: rotate, shift, trim, and, or, xor,
|
765
|
+
invert, max, min, maxmag, minmag, comparetotal, comparetotmag
|