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/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