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