flt 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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