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/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ # Look in the tasks/setup.rb file for the various options that can be
2
+ # configured in this Rakefile. The .rake files in the tasks directory
3
+ # are where the options are used.
4
+
5
+ begin
6
+ require 'bones'
7
+ Bones.setup
8
+ rescue LoadError
9
+ load 'tasks/setup.rb'
10
+ end
11
+
12
+ ensure_in_path 'lib'
13
+ require 'flt/version'
14
+
15
+ task :default => 'spec:run'
16
+
17
+
18
+ PROJ.name = 'flt'
19
+ PROJ.description = "Floating Point Numbers"
20
+ PROJ.authors = 'Javier Goizueta'
21
+ PROJ.email = 'javier@goizueta.info'
22
+ PROJ.version = Flt::VERSION::STRING
23
+ PROJ.rubyforge.name = 'flt'
24
+ PROJ.url = "http://#{PROJ.rubyforge.name}.rubyforge.org"
25
+ PROJ.rdoc.opts = [
26
+ "--main", "README.txt",
27
+ '--title', 'Ruby Flt Documentation',
28
+ "--opname", "index.html",
29
+ "--line-numbers",
30
+ "--inline-source"
31
+ ]
32
+ #PROJ.test.file = 'test/all_tests.rb'
33
+
34
+ # EOF
data/lib/flt.rb ADDED
@@ -0,0 +1,9 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ require 'flt/version'
4
+ require 'flt/support'
5
+ require 'flt/float'
6
+ require 'flt/bigdecimal'
7
+ require 'flt/dec_num'
8
+ require 'flt/bin_num'
9
+ require 'flt/tolerance'
data/lib/flt/b.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'flt/bin_num'
2
+
3
+ B = Flt::BinNum
4
+ def B(*args)
5
+ Flt.BinNum(*args)
6
+ end
@@ -0,0 +1,151 @@
1
+ # Support classes for homogeneous treatment of BigDecimal and Num values by defining BigDecimal.context
2
+
3
+ require 'flt'
4
+ require 'bigdecimal'
5
+ require 'bigdecimal/math'
6
+
7
+ require 'singleton'
8
+
9
+ # Context class with some of the Flt::Num context functionality, to allow the use of BigDecimal numbers
10
+ # similarly to other Num values; this eases the implementation of functions compatible with either
11
+ # Num or BigDecimal values.
12
+ class Flt::BigDecimalContext
13
+
14
+ include Singleton
15
+ # TODO: Context class with precision, rounding, etc. (no singleton)
16
+
17
+ def num_class
18
+ BigDecimal
19
+ end
20
+
21
+ def Num(*args)
22
+ args = *args if args.size==1 && args.first.is_a?(Array)
23
+ if args.size > 1
24
+ BigDecimal.new(DecNum(*args).to_s)
25
+ else
26
+ x = args.first
27
+ case x
28
+ when BigDecimal
29
+ x
30
+ when Rational
31
+ BigDecimal(x.numerator.to_s)/BigDecimal(x.denominator.to_s)
32
+ else
33
+ BigDecimal.new(x.to_s)
34
+ end
35
+ end
36
+ end
37
+
38
+ def radix
39
+ 10
40
+ end
41
+
42
+ # NaN (not a number value)
43
+ def nan
44
+ BigDecimal('0')/BigDecimal('0')
45
+ end
46
+
47
+ def infinity(sign=+1)
48
+ BigDecimal(sign.to_s)/BigDecimal('0')
49
+ end
50
+
51
+ def zero(sign=+1)
52
+ BigDecimal("#{(sign < 0) ? '-' : ''}0")
53
+ end
54
+
55
+ def int_radix_power(n)
56
+ 10**n
57
+ end
58
+
59
+ def exact?
60
+ BigDecimal.limit == 0
61
+ end
62
+
63
+ def precision
64
+ BigDecimal.limit
65
+ end
66
+
67
+ ROUNDING_MODES = {
68
+ BigDecimal::ROUND_UP=>:up,
69
+ BigDecimal::ROUND_DOWN=>:down,
70
+ BigDecimal::ROUND_CEILING=>:ceiling,
71
+ BigDecimal::ROUND_FLOOR=>:floor,
72
+ BigDecimal::ROUND_HALF_UP=>:half_up,
73
+ BigDecimal::ROUND_HALF_DOWN=>:half_down,
74
+ BigDecimal::ROUND_HALF_EVEN=>:half_even
75
+ }
76
+
77
+ def rounding
78
+ ROUNDING_MODES[BigDecimal.mode(BigDecimal::ROUND_MODE, nil)]
79
+ end
80
+
81
+ # Sign: -1 for minus, +1 for plus, nil for nan (note that Float zero is signed)
82
+ def sign(x)
83
+ x.sign < 0 ? -1 : +1
84
+ end
85
+
86
+ # Return copy of x with the sign of y
87
+ def copy_sign(x, y)
88
+ self_sign = x.sign
89
+ other_sign = y.is_a?(Integer) ? (y < 0 ? -1 : +1) : y.sign
90
+ if self_sign && other_sign
91
+ if self_sign == other_sign
92
+ x.to_f
93
+ else
94
+ -x.to_f
95
+ end
96
+ else
97
+ nan
98
+ end
99
+ end
100
+
101
+ def split(x)
102
+ sgn, d, b, e = x.split
103
+ [sgn<0 ? -1 : +1, d.to_i, e-d.size]
104
+ end
105
+
106
+ # Return the value of the number as an signed integer and a scale.
107
+ def to_int_scale(x)
108
+ sgn, d, b, e = x.split
109
+ c = d.to_i
110
+ [sgn<0 ? -1 : c, -c, e-d.size]
111
+ end
112
+
113
+ def special?(x)
114
+ x.nan? || x.infinite?
115
+ end
116
+
117
+ def plus(x)
118
+ x
119
+ end
120
+
121
+ def minus(x)
122
+ -x
123
+ end
124
+
125
+ class << self
126
+
127
+ def big_decimal_method(*methods) #:nodoc:
128
+ methods.each do |method|
129
+ if method.is_a?(Array)
130
+ float_method, context_method = method
131
+ else
132
+ float_method = context_method = method
133
+ end
134
+ define_method(context_method) do |x|
135
+ Num(x).send float_method
136
+ end
137
+ end
138
+ end
139
+
140
+ end
141
+
142
+ big_decimal_method :nan?, :infinite?, :zero?, :abs
143
+
144
+ end
145
+
146
+ # Return a (limited) context object for BigDecimal.
147
+ # This eases the implementation of functions compatible with either Num or BigDecimal values.
148
+ def BigDecimal.context
149
+ Flt::BigDecimalContext.instance
150
+ end
151
+
@@ -0,0 +1,250 @@
1
+ require 'flt/num'
2
+ require 'flt/float'
3
+
4
+ module Flt
5
+
6
+ class BinNum < Num
7
+
8
+ class << self
9
+ # Numerical base.
10
+ def radix
11
+ 2
12
+ end
13
+
14
+ # Integral power of the base: radix**n for integer n; returns an integer.
15
+ def int_radix_power(n)
16
+ (n < 0) ? (2**n) : (1<<n)
17
+ end
18
+
19
+ # Multiply by an integral power of the base: x*(radix**n) for x,n integer;
20
+ # returns an integer.
21
+ def int_mult_radix_power(x,n)
22
+ x * ((n < 0) ? (2**n) : (1<<n))
23
+ end
24
+
25
+ # Divide by an integral power of the base: x/(radix**n) for x,n integer;
26
+ # returns an integer.
27
+ def int_div_radix_power(x,n)
28
+ x / ((n < 0) ? (2**n) : (1<<n))
29
+ end
30
+ end
31
+
32
+ # This is the Context class for Flt::BinNum.
33
+ #
34
+ # The context defines the arithmetic context: rounding mode, precision,...
35
+ #
36
+ # BinNum.context is the current (thread-local) context for DecNum numbers.
37
+ class Context < Num::ContextBase
38
+
39
+ # See Flt::Num::ContextBase#new() for the valid options
40
+ #
41
+ # See also the context constructor method Flt::Num.Context().
42
+ def initialize(*options)
43
+ super(BinNum, *options)
44
+ end
45
+
46
+ # The special values are normalized for binary floats: this keeps the context precision in the values
47
+ # which will be used in conversion to decimal text to yield more natural results.
48
+
49
+ # Normalized epsilon; see Num::Context.epsilon()
50
+ def epsilon(sign=+1)
51
+ super.normalize
52
+ end
53
+
54
+ # Normalized strict epsilon; see Num::Context.epsilon()
55
+ def strict_epsilon(sign=+1)
56
+ super.normalize
57
+ end
58
+
59
+ # Normalized strict epsilon; see Num::Context.epsilon()
60
+ def half_epsilon(sign=+1)
61
+ super.normalize
62
+ end
63
+
64
+ end # BinNum::Context
65
+
66
+ class <<self # BinNum class methods
67
+
68
+ def base_coercible_types
69
+ unless defined? @base_coercible_types
70
+ @base_coercible_types = super.merge(
71
+ Float=>lambda{|x, context|
72
+ if x.nan?
73
+ BinNum.nan
74
+ elsif x.infinite?
75
+ BinNum.infinity(x<0 ? -1 : +1)
76
+ elsif x.zero?
77
+ BinNum.zero(Float.context.sign(x))
78
+ else
79
+ Float.context.split(x)
80
+ end
81
+ }
82
+ )
83
+ end
84
+ @base_coercible_types
85
+ end
86
+
87
+ end
88
+
89
+ # the DefaultContext is the base for new contexts; it can be changed.
90
+ # DefaultContext = BinNum::Context.new(
91
+ # :precision=>113,
92
+ # :emin=> -16382, :emax=>+16383,
93
+ # :rounding=>:half_even,
94
+ # :flags=>[],
95
+ # :traps=>[DivisionByZero, Overflow, InvalidOperation],
96
+ # :ignored_flags=>[],
97
+ # :capitals=>true,
98
+ # :clamp=>true)
99
+ DefaultContext = BinNum::Context.new(
100
+ :exact=>false, :precision=>53, :rounding=>:half_even,
101
+ :emin=> -1025, :emax=>+1023, :flags=>[],
102
+ :traps=>[DivisionByZero, Overflow, InvalidOperation],
103
+ :ignored_flags=>[],
104
+ :capitals=>true,
105
+ :clamp=>true)
106
+
107
+ ExtendedContext = BinNum::Context.new(DefaultContext,
108
+ :traps=>[], :flags=>[], :clamp=>false)
109
+
110
+ IEEEHalfContext = BinNum::Context.new(
111
+ :precision=>1,
112
+ :emin=> -14, :emax=>+15,
113
+ :rounding=>:half_even,
114
+ :flags=>[],
115
+ :traps=>[DivisionByZero, Overflow, InvalidOperation],
116
+ :ignored_flags=>[],
117
+ :capitals=>true,
118
+ :clamp=>true)
119
+
120
+ IEEESingleContext = BinNum::Context.new(
121
+ :precision=>24,
122
+ :emin=> -126, :emax=>+127,
123
+ :rounding=>:half_even,
124
+ :flags=>[],
125
+ :traps=>[DivisionByZero, Overflow, InvalidOperation],
126
+ :ignored_flags=>[],
127
+ :capitals=>true,
128
+ :clamp=>true)
129
+
130
+ IEEEDoubleContext = BinNum::Context.new(
131
+ :precision=>53,
132
+ :emin=> -1022, :emax=>+1023,
133
+ :rounding=>:half_even,
134
+ :flags=>[],
135
+ :traps=>[DivisionByZero, Overflow, InvalidOperation],
136
+ :ignored_flags=>[],
137
+ :capitals=>true,
138
+ :clamp=>true)
139
+
140
+ IEEEQuadContext = BinNum::Context.new(
141
+ :precision=>113,
142
+ :emin=> -16382, :emax=>+16383,
143
+ :rounding=>:half_even,
144
+ :flags=>[],
145
+ :traps=>[DivisionByZero, Overflow, InvalidOperation],
146
+ :ignored_flags=>[],
147
+ :capitals=>true,
148
+ :clamp=>true)
149
+
150
+ IEEEExtendedContext = BinNum::Context.new(
151
+ :precision=>64,
152
+ :emin=> -16382, :emax=>+16383,
153
+ :rounding=>:half_even,
154
+ :flags=>[],
155
+ :traps=>[DivisionByZero, Overflow, InvalidOperation],
156
+ :ignored_flags=>[],
157
+ :capitals=>true,
158
+ :clamp=>true)
159
+
160
+ if Float::RADIX==2
161
+ FloatContext = BinNum::Context.new(
162
+ :precision=>Float::MANT_DIG,
163
+ :rounding=>Support::AuxiliarFunctions.detect_float_rounding,
164
+ :emin=>Float::MIN_EXP-1, :emax=>Float::MAX_EXP-1,
165
+ :flags=>[],
166
+ :traps=>[DivisionByZero, Overflow, InvalidOperation],
167
+ :ignored_flags=>[],
168
+ :capitals=>true,
169
+ :clamp=>true)
170
+ end
171
+
172
+
173
+ # A BinNum value can be defined by:
174
+ # * A String containing a decimal text representation of the number
175
+ # * An Integer
176
+ # * A Rational
177
+ # * A Float
178
+ # * Another BinNum value.
179
+ # * A sign, coefficient and exponent (either as separate arguments, as an array or as a Hash with symbolic keys).
180
+ # This is the internal representation of DecNum, as returned by DecNum#split.
181
+ # The sign is +1 for plus and -1 for minus; the coefficient and exponent are
182
+ # integers, except for special values which are defined by :inf, :nan or :snan for the exponent.
183
+ # * Any other type for which custom conversion is defined in the context.
184
+ #
185
+ # An optional Context can be passed as the last argument to override the current context; also a hash can be passed
186
+ # to override specific context parameters.
187
+ #
188
+ # Except for custome defined conversions and text (String) input, BinNums are constructed with the precision
189
+ # specified by the input parameters (i.e. with the exact value specified by the parameters)
190
+ # and the context precision is ignored. If the BinNum is defined by a decimal text numeral, it is converted
191
+ # to a binary BinNum using the context precision.
192
+ #
193
+ # The Flt.BinNum() constructor admits the same parameters and can be used as a shortcut for DecNum creation.
194
+ def initialize(*args)
195
+ super(*args)
196
+ end
197
+
198
+ def number_of_digits
199
+ @coeff.is_a?(Integer) ? _nbits(@coeff) : 0
200
+ end
201
+
202
+ # Specific to_f conversion TODO: check if it represents an optimization
203
+ if Float::RADIX==2
204
+ def to_f
205
+ if special?
206
+ super
207
+ else
208
+ Math.ldexp(@sign*@coeff, @exp)
209
+ end
210
+ end
211
+ end
212
+
213
+ # Exact BinNum to DecNum conversion: preserve BinNum value.
214
+ #
215
+ # The current DecNum.context determines the valid range and the precision
216
+ # (if its is not :exact the result will be rounded)
217
+ def to_decimal_exact(dec_context=nil)
218
+ Num.convert_exact(self, DecNum, dec_context)
219
+ end
220
+
221
+ # Approximate BinNum to DecNum conversion.
222
+ #
223
+ # Convert to decimal so that if the decimal is converted to a BinNum of the same precision
224
+ # and with same rounding (i.e. BinNum.from_decimal(x, context)) the value of the BinNum
225
+ # is preserved, but use as few decimal digits as possible.
226
+ def to_decimal(*args)
227
+ Num.convert(self, DecNum, *args)
228
+ end
229
+
230
+ # DecNum to BinNum conversion.
231
+ def BinNum.from_decimal(x, binfloat_context=nil)
232
+ Flt.BinNum(x.to_s, binfloat_context)
233
+ end
234
+
235
+ # Unit in the last place: see Flt::Num#ulp()
236
+ #
237
+ # For BinNum the result is normalized
238
+ def ulp(context=nil, mode=:low)
239
+ super(context, mode).normalize(context)
240
+ end
241
+
242
+ end
243
+
244
+ module_function
245
+ def BinNum(*args)
246
+ BinNum.Num(*args)
247
+ end
248
+
249
+
250
+ end # Flt