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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/History.txt +8 -0
  3. data/README.md +765 -0
  4. data/expand.rb +2 -0
  5. data/flt.gemspec +1 -0
  6. data/lib/flt.rb +1 -1
  7. data/lib/flt/bigdecimal.rb +2 -5
  8. data/lib/flt/bin_num.rb +1 -4
  9. data/lib/flt/complex.rb +10 -9
  10. data/lib/flt/dec_num.rb +9 -10
  11. data/lib/flt/float.rb +4 -6
  12. data/lib/flt/math.rb +3 -4
  13. data/lib/flt/num.rb +54 -21
  14. data/lib/flt/sugar.rb +1 -2
  15. data/lib/flt/support.rb +5 -2
  16. data/lib/flt/support/flag_values.rb +1 -1
  17. data/lib/flt/support/rationalizer.rb +3 -3
  18. data/lib/flt/support/rationalizer_extra.rb +6 -6
  19. data/lib/flt/support/reader.rb +1 -1
  20. data/lib/flt/tolerance.rb +2 -7
  21. data/lib/flt/trigonometry.rb +0 -2
  22. data/lib/flt/version.rb +1 -1
  23. data/setup.rb +1 -2
  24. data/test/generate_trig_data.rb +6 -6
  25. data/test/helper.rb +11 -5
  26. data/test/reader.rb +1 -1
  27. data/test/test_base_digits.rb +4 -2
  28. data/test/test_basic.rb +7 -7
  29. data/test/test_big_decimal.rb +21 -21
  30. data/test/test_bin.rb +1 -1
  31. data/test/test_bin_arithmetic.rb +1 -1
  32. data/test/test_binfloat_conversion.rb +1 -1
  33. data/test/test_coercion.rb +1 -1
  34. data/test/test_comparisons.rb +4 -4
  35. data/test/test_dectest.rb +2 -2
  36. data/test/test_define_conversions.rb +10 -10
  37. data/test/test_epsilon.rb +1 -1
  38. data/test/test_exact.rb +12 -11
  39. data/test/test_flags.rb +1 -1
  40. data/test/test_float.rb +23 -25
  41. data/test/test_format.rb +1 -1
  42. data/test/test_formatter.rb +6 -8
  43. data/test/test_hex_format.rb +2 -2
  44. data/test/test_multithreading.rb +1 -1
  45. data/test/test_normalized.rb +1 -1
  46. data/test/test_num_constructor.rb +6 -1
  47. data/test/test_odd_even.rb +1 -1
  48. data/test/test_rationalizer.rb +1 -1
  49. data/test/test_round.rb +36 -1
  50. data/test/test_sugar.rb +6 -6
  51. data/test/test_to_int.rb +4 -4
  52. data/test/test_to_rf.rb +126 -1
  53. data/test/test_tol.rb +1 -1
  54. data/test/test_trig.rb +1 -1
  55. data/test/test_ulp.rb +3 -3
  56. metadata +18 -4
  57. data/README.rdoc +0 -614
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 84bc5146381ff127868320f22bb421bc8d1ad16a
4
- data.tar.gz: 66f6fc689cb2297638127a1217fe4ee3e19b8aa7
3
+ metadata.gz: 4944139b67630d8b78c7f29d004a71b79003e757
4
+ data.tar.gz: '0290ba88b15d6b1b0d050fe62cbd4cdaeb4cc856'
5
5
  SHA512:
6
- metadata.gz: 10bc77b0f1ab349a36abcf4512aa9a16aecc886e4da95ec2c6edc180c1c00b74113f98d2673f1c2aaae3cf7856b1be8291be0e59f2e9082f541cceae8c5f2130
7
- data.tar.gz: 1279653a5e9471834c3f48819db261bcb5cbf4e749ded3cc0d039a3e4bb3572eb20323b90fb0058739630b6bf8cd6eb36a6132713960da8f7b6c2919fca79122
6
+ metadata.gz: c7c630691b5ca086da656641ca04a7ef3f163c16661bb2be6cd32785980c5facbf3fdf2779bce955f72e6352ad43d95435497b74428f0abe30eb745b6559e364
7
+ data.tar.gz: 071b9592aec3c4edc7994f922722c7cd81cfd96d06430569432df8de135500c459842eba8d98e20705120ed913aabe5f3ef69a02e56ac43c6320be90f68cfd71
@@ -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
@@ -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