flt 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. data/History.txt +41 -0
  2. data/License.txt +20 -0
  3. data/Manifest.txt +42 -0
  4. data/README.txt +557 -0
  5. data/Rakefile +34 -0
  6. data/lib/flt.rb +9 -0
  7. data/lib/flt/b.rb +6 -0
  8. data/lib/flt/bigdecimal.rb +151 -0
  9. data/lib/flt/bin_num.rb +250 -0
  10. data/lib/flt/d.rb +6 -0
  11. data/lib/flt/dec_num.rb +1239 -0
  12. data/lib/flt/float.rb +458 -0
  13. data/lib/flt/math.rb +66 -0
  14. data/lib/flt/num.rb +4211 -0
  15. data/lib/flt/sugar.rb +102 -0
  16. data/lib/flt/support.rb +1335 -0
  17. data/lib/flt/tolerance.rb +561 -0
  18. data/lib/flt/tolerance/sugar.rb +77 -0
  19. data/lib/flt/version.rb +9 -0
  20. data/setup.rb +1585 -0
  21. data/tasks/ann.rake +80 -0
  22. data/tasks/bones.rake +20 -0
  23. data/tasks/gem.rake +192 -0
  24. data/tasks/git.rake +40 -0
  25. data/tasks/manifest.rake +48 -0
  26. data/tasks/notes.rake +27 -0
  27. data/tasks/post_load.rake +39 -0
  28. data/tasks/rdoc.rake +50 -0
  29. data/tasks/rubyforge.rake +55 -0
  30. data/tasks/setup.rb +279 -0
  31. data/tasks/spec.rake +54 -0
  32. data/tasks/svn.rake +47 -0
  33. data/tasks/test.rake +40 -0
  34. data/test/all_tests.rb +23 -0
  35. data/test/helper.rb +101 -0
  36. data/test/reader.rb +68 -0
  37. data/test/test_basic.rb +396 -0
  38. data/test/test_bin.rb +245 -0
  39. data/test/test_bin_arithmetic.rb +94 -0
  40. data/test/test_binfloat_conversion.rb +24 -0
  41. data/test/test_coercion.rb +22 -0
  42. data/test/test_comparisons.rb +53 -0
  43. data/test/test_dectest.rb +216 -0
  44. data/test/test_define_conversions.rb +144 -0
  45. data/test/test_epsilon.rb +55 -0
  46. data/test/test_exact.rb +147 -0
  47. data/test/test_flags.rb +34 -0
  48. data/test/test_multithreading.rb +32 -0
  49. data/test/test_num_constructor.rb +133 -0
  50. data/test/test_odd_even.rb +78 -0
  51. data/test/test_round.rb +104 -0
  52. data/test/test_to_int.rb +104 -0
  53. data/test/test_to_rf.rb +36 -0
  54. data/test/test_tol.rb +102 -0
  55. data/test/test_ulp.rb +127 -0
  56. metadata +147 -0
data/History.txt ADDED
@@ -0,0 +1,41 @@
1
+ == 1.0.0 2009-07-22
2
+
3
+ * First release of the new Flt project (derived from the former Ruby-Decimal.)
4
+ - New gem name: flt
5
+ - New organization in Flt namespace
6
+ - Arbitrary base support; Flt::DecNum replaces Decimal; new classes BinNum, Num[b].
7
+ - Tolerance objects to define floating-point tolerances.
8
+ - Free and fixed input/output formats.
9
+ - Contexts for Float and BigDecimal compatibility with Flt::Num
10
+
11
+ == 0.2.2 2009-07-21
12
+
13
+ * Bug fixes:
14
+ - Decimal#normal? was incorrect for special values and zero.
15
+ - After a local context, the global context was set to a copy of the original
16
+ context, so any previously stored reference to it was now unbound.
17
+ - Context#normalize was incorrect.
18
+
19
+ == 0.2.1 2009-06-23
20
+
21
+ * Bug fixes:
22
+ - DecNum#inspect was always producing debug information; now it uses $DEBUG
23
+ - Raising some exceptions caused error, because too many parameters were being
24
+ passed to the exception's base class initializer.
25
+
26
+ * New functionality:
27
+ - ulp (unit in the last place)
28
+ - normalize (in the classic sense, not in the original GDA sense)
29
+ - maximum_finite, minimum_normal, minimum_nonzero value decimal constructors
30
+ - epsilon, strict_epsilon, half_epsilon
31
+ - setting context exponent limits with elimit
32
+ - require decimal/shortcut to use D for DecNum
33
+
34
+ == 0.2.0 2009-06-21
35
+
36
+ * New functions implemented:
37
+ exp(), ln(), log10(), power().
38
+
39
+ == 0.1.0 2009-06-19
40
+
41
+ * Initial release as ruby-decimal
data/License.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007 Javier Goizueta
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest.txt ADDED
@@ -0,0 +1,42 @@
1
+ History.txt
2
+ License.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ lib/flt.rb
7
+ lib/flt/version.rb
8
+ lib/flt/num.rb
9
+ lib/flt/support.rb
10
+ lib/flt/dec_num.rb
11
+ lib/flt/math.rb
12
+ lib/flt/bin_num.rb
13
+ lib/flt/float.rb
14
+ lib/flt/bigdecimal.rb
15
+ lib/flt/tolerance.rb
16
+ lib/flt/tolerance/sugar.rb
17
+ lib/flt/d.rb
18
+ lib/flt/b.rb
19
+ lib/flt/sugar.rb
20
+ setup.rb
21
+ test/all_tests.rb
22
+ test/helper.rb
23
+ test/test_basic.rb
24
+ test/test_coercion.rb
25
+ test/test_comparisons.rb
26
+ test/test_define_conversions.rb
27
+ test/test_flags.rb
28
+ test/test_multithreading.rb
29
+ test/test_dectest.rb
30
+ test/test_round.rb
31
+ test/test_exact.rb
32
+ test/test_to_int.rb
33
+ test/test_to_rf.rb
34
+ test/test_epsilon.rb
35
+ test/test_ulp.rb
36
+ test/test_odd_even.rb
37
+ test/test_bin.rb
38
+ test/test_binfloat_conversion.rb
39
+ test/test_bin_arithmetic.rb
40
+ test/test_tol.rb
41
+ test/reader.rb
42
+ test/test_num_constructor.rb
data/README.txt ADDED
@@ -0,0 +1,557 @@
1
+ = Introduction
2
+
3
+ This library provides arbitrary precision floating-point types for Ruby. All types and
4
+ functions are within a namespace called Flt. Decimal and Binary floating point numbers
5
+ are implemented in classes Flt::DecNum and Flt::BinNum. These types are completely
6
+ written in Ruby using the multiple precision native integers. The performance
7
+ could be improved in the future by using a C extension based on the decNumber libray.
8
+
9
+ The Flt::Tolerance classes and the Flt.Tolerance() constructor handle floating point
10
+ tolerances defined in flexible ways.
11
+
12
+ Context classes are define in the files
13
+ flt/float.rb[link:files/lib/flt/float_rb.html] and
14
+ flt/bigdecimal.rb[link:files/lib/flt/bigdecimal_rb.html]
15
+ for Float and BigDecimal numbers that aid to the interchangeability of floating point types. This
16
+ represent the only definition of identifiers outside the Flt namespace: the methods
17
+ Float.context() and BigDecimal.context() and some contants in Float.
18
+
19
+ This library is the successor of the ruby-decimal gem, that defined the Decimal class
20
+ for decimal floating point; that class has been renamed to Flt::DecNum and support
21
+ has been added for binary floating point and tolerances.
22
+
23
+ The documentation for this package is available at http://flt.rubyforge.org/
24
+
25
+ The code is at http://github.com/jgoizueta/flt/
26
+
27
+ = DecNum
28
+
29
+ Flt::DecNum is a standards-compliant arbitrary precision decimal floating-point type for Ruby.
30
+ It is based on the Python Decimal class.
31
+
32
+ == Standars compliance.
33
+
34
+ DecNum pretends to be conformant to the General Decimal Arithmetic Specification
35
+ and the revised IEEE 754 standard (IEEE 754-2008).
36
+
37
+ = Examples of use
38
+
39
+ To install the library use gem from the command line: (you may not need +sudo+)
40
+ FIXME: The flt gem is not publicly available yet.
41
+ sudo gem install flt
42
+
43
+ Then require the library in your code (if it fails you may need to <tt>require 'rubygems'</tt> first)
44
+ require 'flt'
45
+ include Flt
46
+
47
+ Now we can use the DecNum class simply like this:
48
+
49
+ puts DecNum(1)/DecNum(3) # -> 0.3333333333333333333333333333
50
+
51
+ DecNum() is a constructor that can be used instead of DecNum.new()
52
+
53
+ == Contexts
54
+
55
+ Contexts are environments for arithmetic operations. They govern precision, set rules
56
+ for rounding, determine which signals are treated as exceptions, and limit the range
57
+ for exponents.
58
+
59
+ Each thread has an active context that can be accessed like this:
60
+
61
+ puts DecNum.context.precision # -> 28
62
+
63
+ The active context can be globally for the current thread:
64
+
65
+ DecNum.context.precision = 2
66
+ puts DecNum.context.precision # -> 2
67
+ puts DecNum(1)/DecNum(3) # -> 0.33
68
+ DecNum.context.precision += 7
69
+ puts DecNum.context.precision # -> 9
70
+ puts DecNum(1)/DecNum(3) # -> 0.333333333
71
+
72
+ Or it can be altered locally inside a block:
73
+
74
+ DecNum.context do
75
+ DecNum.context.precision = 5
76
+ puts DecNum.context.precision # -> 5
77
+ end
78
+ puts DecNum.context.precision # -> 9
79
+
80
+ The block for a local context can be passed the current context as an argument:
81
+
82
+ DecNum.context do |local_context|
83
+ local_context.precision = 5
84
+ puts DecNum.context.precision # -> 5
85
+ end
86
+ puts DecNum.context.precision # -> 9
87
+
88
+ A context object can be used to define the local context:
89
+
90
+ my_context = DecNum::Context(:precision=>20)
91
+ DecNum.context(my_context) do |context|
92
+ puts context.precision
93
+ end # -> 20
94
+
95
+ And individual parameters can be assigned like this:
96
+
97
+ puts DecNum.context.precision # -> 9
98
+ puts DecNum.context.rounding # -> half_even
99
+ DecNum.context(:rounding=>:down) do |context|
100
+ puts context.precision # -> 9
101
+ puts context.rounding # -> down
102
+ end
103
+
104
+
105
+ Contexts created with the DecNum::Context() constructor
106
+ inherit from DecNum::DefaultContext.
107
+ Default context attributes can be established by modifying
108
+ that object:
109
+
110
+ DecNum::DefaultContext.precision = 10
111
+ DecNum.context = DecNum::Context(:rounding=>:half_up)
112
+ puts DecNum.context.precision # -> 10
113
+
114
+ Note that a context object assigned to DecNum.context is copied,
115
+ so it is not altered through DecNum.context:
116
+
117
+ puts my_context.precision # -> 20
118
+ DecNum.context = my_context
119
+ DecNum.context.precision = 2
120
+ puts my_context.precision # -> 20
121
+
122
+ So, DefaultContext is not altered when modifying DecNum.context.
123
+
124
+ Methods that use a context have an optional parameter to override
125
+ the active context (DecNum.context) :
126
+
127
+ DecNum.context.precision = 3
128
+ puts DecNum(1).divide(3) # -> 0.333
129
+ puts DecNum(1).divide(3, my_context) # -> 0.33333333333333333333
130
+
131
+ Individual context parameters can also be overriden:
132
+
133
+ puts DecNum(1).divide(3, :precision=>6) # -> 0.333333
134
+
135
+ There are two additional predefined contexts DecNum::ExtendedContext
136
+ and DecNum::BasicContext that are not meant to be modified; they
137
+ can be used to achieve reproducible results. We will use
138
+ DecNum::ExtendedContext in the following examples:
139
+
140
+ DecNum.context = DecNum::ExtendedContext
141
+
142
+ Most decimal operations can be executed by using either Context or DecNum methods:
143
+
144
+ puts DecNum.context.exp(1) # -> 2.71828183
145
+ puts DecNum(1).exp # -> 2.71828183
146
+
147
+ If using Context methods, values are automatically converted as if the DecNum() constructor
148
+ was used.
149
+
150
+ ==Rounding
151
+
152
+ Results are normally rounded using the precision (number of significant digits)
153
+ and rounding mode defined in the context.
154
+
155
+ DecNum.context.precision = 4
156
+ puts DecNum(1)/DecNum(3) # -> 0.3333
157
+ puts DecNum('1E20')-DecNum('1E-20') # -> 1.000E+20
158
+ DecNum.context.rounding = :half_up
159
+ puts +DecNum('100.05') # -> 100.1
160
+ DecNum.context.rounding = :half_even
161
+ puts +DecNum('100.05') # -> 100.0
162
+
163
+ Note that input values are not rounded, only results; we use
164
+ the plus operator to force rounding here:
165
+
166
+ DecNum.context.precision = 4
167
+ x = DecNum('123.45678')
168
+ puts x # -> 123.45678
169
+ puts +x # -> 123.5
170
+
171
+ Precision can be also set to exact to avoid rounding, by using
172
+ the exact property or using a 0 precision. In exact mode results
173
+ are never rounded and results that have an infinite number of
174
+ digits trigger the DecNum::Inexact exception.
175
+
176
+ DecNum.context.exact = true
177
+ puts DecNum('1E20')-DecNum('1E-20') # -> 99999999999999999999.99999999999999999999
178
+ puts DecNum(16).sqrt # -> 4
179
+ puts DecNum(16)/DecNum(4) # -> 4
180
+ puts DecNum(1)/DecNum(3) # -> Exception : Flt::Num::Inexact
181
+
182
+ DecNum.context.precision = 5
183
+ puts DecNum('1E20')-DecNum('1E-20') # -> 1.0000E+20
184
+ puts DecNum(16).sqrt # -> 4
185
+ puts DecNum(16)/DecNum(4) # -> 4
186
+ puts DecNum(1)/DecNum(3) # -> 0.33333
187
+
188
+ There are also some methods for explicit rounding that provide
189
+ an interface compatible with the Ruby interface of Float:
190
+
191
+ puts DecNum('101.5').round # -> 102
192
+ puts DecNum('101.5').round(0) # -> 102
193
+ puts DecNum('101.12345').round(2) # -> 101.12
194
+ puts DecNum('101.12345').round(-1) # -> 1.0E+2
195
+ puts DecNum('101.12345').round(:places=>2) # -> 101.12
196
+ puts DecNum('101.12345').round(:precision=>2) # -> 1.0E+2
197
+ puts DecNum('101.5').round(:rounding=>:half_up) # -> 102
198
+ puts DecNum('101.5').ceil # -> 102
199
+ puts DecNum('101.5').floor # -> 101
200
+ puts DecNum('101.5').truncate # -> 101
201
+
202
+ ==Special values
203
+
204
+ In addition to finite numbers, a DecNum object can represent some special values:
205
+ * Infinity (+Infinity, -Infinity). The method DecNum#infinite? returns true for these to values.
206
+ DecNum.infinity DecNum.infinity(-1) can be used to get these values.
207
+ * NaN (not a number) represents indefined results. The method DecNum#nan? returns true for it and
208
+ DecNum.nan can be used to obtain it. There is a variant, sNaN (signaling NaN) that casues
209
+ an invalid operation condition if used; it can be detected with DecNum.snan?.
210
+ A NaN can also include diagnostic information in its sign and coefficient.
211
+
212
+ Any of the special values can be detected with DecNum#special?
213
+ Finite numbers can be clasified with
214
+ these methods:
215
+ * DecNum#zero? detects a zero value (note that there are two zero values: +0 and -0)
216
+ * DecNum#normal? detects normal values: those whose adjusted exponents are not less than the the emin.
217
+ * DecNum#subnormal? detects subnormal values: those whose adjusted exponents are less than the the emin.
218
+
219
+ ==Exceptions
220
+
221
+ Exceptional conditions that may arise during operations have corresponding classes that represent them:
222
+ * DecNum::InvalidOperation
223
+ * DecNum::DivisionByZero
224
+ * DecNum::DivisionImpossible
225
+ * DecNum::DivisionUndefined
226
+ * DecNum::Inexact
227
+ * DecNum::Overflow
228
+ * DecNum::Underflow
229
+ * DecNum::Clamped
230
+ * DecNum::InvalidContext
231
+ * DecNum::Rounded
232
+ * DecNum::Subnormal
233
+ * DecNum::ConversionSyntax
234
+
235
+ For each condition, a flag and a trap (boolean values) exist in the context.
236
+ When a condition occurs, the corresponding flag in the context takes the value true (and remains
237
+ set until cleared) and a exception is raised if the corresponding trap has the value true.
238
+
239
+ DecNum.context.traps[DecNum::DivisionByZero] = false
240
+ DecNum.context.flags[DecNum::DivisionByZero] = false
241
+ puts DecNum(1)/DecNum(0) # -> Infinity
242
+ puts DecNum.context.flags[DecNum::DivisionByZero] # -> true
243
+ DecNum.context.traps[DecNum::DivisionByZero] = true
244
+ puts DecNum(1)/DecNum(0) # -> Exception : Flt::Num::DivisionByZero
245
+
246
+ ==Numerical conversion
247
+
248
+ By default, DecNum is interoperable with Integer and Rational.
249
+ Conversion happens automatically to operands:
250
+
251
+ puts DecNum('0.1') + 1 # -> 1.1
252
+ puts 7 + DecNum('0.2') # -> 7.2
253
+ puts Rational(5,2) + DecNum('3') # -> 5.5
254
+
255
+ Conversion can also be done explicitely with
256
+ the DecNum constructor:
257
+
258
+ puts DecNum(7) # -> 7
259
+ puts DecNum(Rational(1,10)) # -> 0.1
260
+
261
+ Converting a DecNum to other numerical types can be done with specific Ruby-style methods.
262
+
263
+ puts DecNum('1.1').to_i # -> 1
264
+ puts DecNum('1.1').to_r # -> 11/10
265
+
266
+ (note the truncated result of to_i)
267
+ Or with a generic method:
268
+ puts DecNum('1.1').convert_to(Integer) # -> 1
269
+ puts DecNum('1.1').convert_to(Rational) # -> 11/10
270
+
271
+ Thera are also GDAS style conversion operations:
272
+
273
+ puts DecNum('1.1').to_integral_value # -> 1
274
+
275
+ And conversion is also possible to Float:
276
+ puts DecNum('1.1').to_f # -> 1.1
277
+ puts DecNum('1.1').convert_to(Float) # -> 1.1
278
+ puts Float(DecNum('1.1')) # -> 1.1
279
+
280
+ Types with predefined bidirectional conversion (Integer and Rational)
281
+ can be operated with DecNum on either side of an operator, and the result will be a DecNum.
282
+ For Float there is no predefined bidirectional conversion (see below how to define it)
283
+ and the result of an operation between DecNum and Float will be of type Float.
284
+
285
+ puts (DecNum('1.1') + 2.0).class # -> Float
286
+ puts (2.0 + DecNum('1.1')).class # -> Float
287
+
288
+ The conversion system is extensible. For example, we can include BigDecimal into it
289
+ by defining suitable conversion procedures:
290
+
291
+ DecNum.context.define_conversion_from(BigDecimal) do |x, context|
292
+ DecNum(x.to_s)
293
+ end
294
+ DecNum.context.define_conversion_to(BigDecimal) do |x|
295
+ BigDecimal.new(x.to_s)
296
+ end
297
+
298
+ Now we can mix BigDecimals and Decimals in expressions and convert from DecNum
299
+ to BigDecimal:
300
+
301
+ puts BigDecimal.new('1.1') + DecNum('2.2') # -> 3.3
302
+ puts DecNum('1.1').convert_to(BigDecimal) # -> 0.11E1
303
+
304
+ Note that the conversions are defined in a Context object and will be available only
305
+ when that context applies. That way we can define conversions for specific purposes
306
+ without affecting a program globally.
307
+
308
+ As another example consider conversion from Float to DecNum, which is not defined by
309
+ default because it can be defined in different ways depending on the purpose.
310
+
311
+ A Float constant such as 0.1 defines a Float object which has a numerical value close to,
312
+ but not exactly 1/10. When converting that Float to DecNum we could decide to preserve
313
+ the exact numerical value of the number or try to find a simple decimal expression within
314
+ a given tolerance. If we take the first approach we can define this conversion:
315
+
316
+ DecNum.context.define_conversion_from(Float) do |x, context|
317
+ s,e = Math.frexp(x)
318
+ s = Math.ldexp(s, Float::MANT_DIG).to_i
319
+ e -= Float::MANT_DIG
320
+ DecNum(s*(Float::RADIX**e))
321
+ end
322
+
323
+ Note that the conversion we've defined depends on the context precision:
324
+
325
+ DecNum.local_context(:precision=>20) { puts DecNum(0.1) } # -> 0.10000000000000000555
326
+
327
+ DecNum.local_context(:precision=>12) { puts DecNum(0.1) } # -> 0.100000000000
328
+
329
+ DecNum.local_context(:exact=>true) { puts DecNum(0.1) } # -> 0.1000000000000000055511151231257827021181583404541015625
330
+
331
+ A different approach for Float to DecNum conversion is to find the shortest (fewer digits) DecNum
332
+ that rounds to the Float with the binary precision that the Float has.
333
+ We will assume that the DecNum to Float conversion done with the rounding mode of the DecNum context.
334
+ The BinNum class has a method to perform this kind of conversion, so we will use it.
335
+
336
+ DecNum.context.define_conversion_from(Float) do |x, dec_context|
337
+ BinNum.context(:rounding=>dec_context.rounding) do |bin_context|
338
+ BinNum(x).to_decimal
339
+ end
340
+ end
341
+
342
+ The result is independent of the context precision.
343
+
344
+ puts DecNum(0.1) # -> 0.1
345
+ puts DecNum(1.0/3) # -> 0.3333333333333333
346
+
347
+ This conversion gives the results expected most of the time, but it must be noticed that
348
+ there must be some compromise, because different decimal literals convert to the same Float value:
349
+
350
+ puts DecNum(0.10000000000000001) # -> 0.1
351
+
352
+ There's also some uncertainty because the way the Ruby interpreter parses Float literals
353
+ may not be well specified; in the usual case (IEEE Double Floats and round-to-even)
354
+ the results will be as expected (correctly rounded Floats), but some platforms may
355
+ behave differently.
356
+
357
+ The BinNum also a instance method +to_decimal_exact+ to perform the previous 'exact' conversion, that
358
+ could have be written:
359
+
360
+ DecNum.context.define_conversion_from(Float) do |x, context|
361
+ DecNum.context(context) do
362
+ BinNum(x).to_decimal_exact
363
+ end
364
+ end
365
+
366
+ == Abbreviation
367
+
368
+ The use of DecNum can be made less verbose by requiring:
369
+
370
+ require 'flt/d'
371
+
372
+ This file defines +D+ as a synonym for +DecNum+:
373
+
374
+ D.context.precision = 3
375
+ puts +D('1.234') # -> 1.23
376
+
377
+ Some convenient methods are added to numeric classes by requiring the optional
378
+ flt/sugar.rb[link:files/lib/flt/sugar_rb.html]. This must be explicitely required
379
+ because it could cause conflicts with other extensions of these classes.
380
+
381
+ require 'flt/sugar'
382
+
383
+ puts 34.odd? # -> false
384
+ puts 34.even? # -> true
385
+ puts 0.1.split.inspect # -> [1, 7205759403792794, -56]
386
+ puts (-0.1).sign # -> -1
387
+
388
+ == Error analysis
389
+
390
+ The DecNum#ulp() method returns the value of a "unit in the last place" for a given number under
391
+ the current context.
392
+
393
+ D.context.precision = 4
394
+ puts D('1.5').ulp # -> 0.001
395
+ puts D('1.5E10').ulp # -> 1E+7
396
+
397
+ Whe can compute the error in ulps of an approximation +aprx+ to correclty rounded value +exct+ with:
398
+
399
+ def ulps(exct, aprx)
400
+ (aprx-exct).abs/exct.ulp
401
+ end
402
+
403
+ puts ulps(DecNum('0.5000'), DecNum('0.5003')) # -> 3
404
+ puts ulps(DecNum('0.5000'), DecNum('0.4997')) # -> 3
405
+
406
+ puts ulps(DecNum('0.1000'), DecNum('0.1003')) # -> 3E+1
407
+ puts ulps(DecNum('0.1000'), DecNum('0.0997')) # -> 3E+1
408
+
409
+ puts ulps(DecNum(1), DecNum(10).next_minus) # -> 8.999E+4
410
+ puts ulps(DecNum(1), DecNum(10).next_plus) # -> 9.01E+4
411
+
412
+ Note that in the definition of ulps we use exct.ulp. If we had use aprx.ulp DecNum(10).next_plus
413
+ would seem to be a better approximation to DecNum(1) than DecNum(10).next_minus. (Admittedly,
414
+ such bad approximations should not be common.)
415
+
416
+ == BinNum Input/Output
417
+
418
+ BinNum can be defined with a decimal string literal and converted to one with to_s, as DecNum,
419
+ but since this involves a change of base these are inexact operations subject to some specific precision limits.
420
+
421
+ If we define the number with a binary literal, no base conversion is involved and the result is exactly defined;
422
+ here we define a number with just one bit of precision:
423
+
424
+ x = BinNum('0.001', :base=>2)
425
+ puts x.number_of_digits # -> 1
426
+ puts x.to_s(:base=>2) # -> 0.001
427
+
428
+ Note that we could have defined it with more precision, e.g.
429
+
430
+ y = BinNum('0.001000', :base=>2)
431
+ puts y.number_of_digits # -> 4
432
+ puts y.to_s(:base=>2) # -> 0.001000
433
+
434
+ But let's get back to our one bit quantity, x, and convert it to a decimal string. Since the internal precision
435
+ is only one bit it contains very little information:
436
+
437
+ puts x # -> 0.1
438
+
439
+ We can obtain more digits with the :all_digits option which show all the decimal digits that are significative
440
+ for the given precision of 1 bit:
441
+
442
+ puts x.to_s(:all_digits=>true) # -> 0.12
443
+
444
+ We can also obtain the exact value of x by using the Num.convert_exact method to convert it to decimal (base 10):
445
+
446
+ puts Num.convert_exact(x,10) # -> 0.125
447
+
448
+ Let's convert the default decimal output back to another BinNum which will preserve its precision:
449
+
450
+ y = BinNum(x.to_s)
451
+
452
+ The result may seem ok:
453
+
454
+ puts y # -> 0.1
455
+
456
+ But is not exactly what we originally had:
457
+
458
+ puts y==x # -> false
459
+ puts y # -> 0.1
460
+ puts y.number_of_digits # -> 5
461
+ puts y.to_s(:base=>2) # -> 0.00011010
462
+
463
+ The new value y has gained some digits because of the intermediate conversion to decimal: one decimal digit
464
+ contains more information than one bit, and the result shows that.
465
+
466
+ If we wanted to preserve exactly the number we should have done this:
467
+
468
+ y = BinNum(x.to_s, :fixed, :precision=>x.number_of_digits)
469
+ puts y==x # -> true
470
+
471
+ To preserve the value we had to explicitly determine how many bits should y have.
472
+
473
+ With the :fixed options the number produced by BinNum is rounded to the context precision (which can be
474
+ overriden as in the example by other options):
475
+
476
+ puts BinNum(x.to_s, :fixed, :precision=>32).to_s(:base=>2) # -> 0.00011001100110011001100110011001101
477
+ puts BinNum(x.to_s, :fixed, :precision=>1).to_s(:base=>2) # -> 0.001
478
+
479
+ Note also that if we normalize a value we will change it's precision to that of the context:
480
+
481
+ puts x.number_of_digits # -> 1
482
+ puts x.normalize.number_of_digits # -> 53
483
+ puts x.normalize.to_s # -> 0.125
484
+
485
+ == More Information
486
+
487
+ * Decimal Floating point type: see the base Flt::Num class and the Flt::DecNum class
488
+ * Binary Floating point type: see the base Flt::Num class and the Flt::BinNum class
489
+ * Floating Point Contexts: see documentation for classes Flt::Num::ContextBase,
490
+ Flt::DecNum::Context and Flt::BinNum::Context
491
+ * Floating Point Tolerance: see the flt/tolerance.rb[link:files/lib/flt/tolerance_rb.html] file
492
+ and the Flt::Tolerance class
493
+ * Constructors: see Flt.DecNum(), Flt.BinNum() and Flt.Tolerance().
494
+
495
+ = DecNum vs BigDecimal
496
+
497
+ --
498
+ EXPAND-
499
+ ++
500
+
501
+ DecNum solves some of the difficulties of using BigDecimal.
502
+
503
+ One of the major problems with BigDecimal is that it's not easy to control the number of
504
+ significant digits of the results. While addition, subtraction and multiplication are exact (unless a limit is used),
505
+ divisions will need to be passed precision explicitly or else an indeterminate number of significant digits will be lost.
506
+ Part of the problem is that numbers don't keep track of its precision (0.1000 is not distinguishable from 0.1.)
507
+
508
+ With DecNum, Context objects are used to specify the exact number of digits to be used for all operations making
509
+ the code cleaner and the results more easily predictable.
510
+
511
+ DecNum.context.precision = 10
512
+ puts DecNum(1)/DecNum(3)
513
+ Contexts are thread-safe and can be used for individual operations:
514
+ puts DecNum(1).divide(DecNum(e), DecNum::Context(:precision=>4))
515
+ Which can be abbreviated:
516
+ puts DecNum(1).divide(DecNum(e), :precision=>4)
517
+ Or use locally in a block without affecting other code:
518
+ DecNum.context {
519
+ DecNum.context.precision = 3
520
+ puts DecNum(1)/DecNum(3)
521
+ }
522
+ puts DecNum.context.precision
523
+ Which can also be abbreviated:
524
+ DecNum.context(:precision=>3) { puts DecNum(1)/DecNum(3) }
525
+
526
+ This allows in general to write simpler code; e.g. this is an exponential function, adapted from the
527
+ 'recipes' in Python's Decimal:
528
+ def exp(x, c=nil)
529
+ i, lasts, s, fact, num = 0, 0, 1, 1, 1
530
+ DecNum.context(c) do |context|
531
+ context.precision += 2
532
+ while s != lasts
533
+ lasts = s
534
+ i += 1
535
+ fact *= i
536
+ num *= x
537
+ s += num / fact
538
+ end
539
+ end
540
+ return +s
541
+ end
542
+
543
+ The final unary + applied to the result forces it to be rounded to the current precision
544
+ (because we have computed it with two extra digits)
545
+ The result of this method does not have trailing non-significant digits, as is common with BigDecimal
546
+ (e.g. in the exp implementation available in the standard Ruby library, in bigdecimal/math)
547
+
548
+ --
549
+ EXPAND+
550
+ ++
551
+
552
+ = Roadmap
553
+
554
+ * Version 1.0.0: First released version of the new flt gem.
555
+ * Version 1.1.0: Implement the missing GDA functions:
556
+ rotate, shift, trim, and, or, xor, invert,
557
+ max, min, maxmag, minmag, comparetotal, comparetotmag