flt 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +41 -0
- data/License.txt +20 -0
- data/Manifest.txt +42 -0
- data/README.txt +557 -0
- data/Rakefile +34 -0
- data/lib/flt.rb +9 -0
- data/lib/flt/b.rb +6 -0
- data/lib/flt/bigdecimal.rb +151 -0
- data/lib/flt/bin_num.rb +250 -0
- data/lib/flt/d.rb +6 -0
- data/lib/flt/dec_num.rb +1239 -0
- data/lib/flt/float.rb +458 -0
- data/lib/flt/math.rb +66 -0
- data/lib/flt/num.rb +4211 -0
- data/lib/flt/sugar.rb +102 -0
- data/lib/flt/support.rb +1335 -0
- data/lib/flt/tolerance.rb +561 -0
- data/lib/flt/tolerance/sugar.rb +77 -0
- data/lib/flt/version.rb +9 -0
- data/setup.rb +1585 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +192 -0
- data/tasks/git.rake +40 -0
- data/tasks/manifest.rake +48 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +39 -0
- data/tasks/rdoc.rake +50 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +279 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +40 -0
- data/test/all_tests.rb +23 -0
- data/test/helper.rb +101 -0
- data/test/reader.rb +68 -0
- data/test/test_basic.rb +396 -0
- data/test/test_bin.rb +245 -0
- data/test/test_bin_arithmetic.rb +94 -0
- data/test/test_binfloat_conversion.rb +24 -0
- data/test/test_coercion.rb +22 -0
- data/test/test_comparisons.rb +53 -0
- data/test/test_dectest.rb +216 -0
- data/test/test_define_conversions.rb +144 -0
- data/test/test_epsilon.rb +55 -0
- data/test/test_exact.rb +147 -0
- data/test/test_flags.rb +34 -0
- data/test/test_multithreading.rb +32 -0
- data/test/test_num_constructor.rb +133 -0
- data/test/test_odd_even.rb +78 -0
- data/test/test_round.rb +104 -0
- data/test/test_to_int.rb +104 -0
- data/test/test_to_rf.rb +36 -0
- data/test/test_tol.rb +102 -0
- data/test/test_ulp.rb +127 -0
- 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
data/lib/flt/b.rb
ADDED
@@ -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
|
+
|
data/lib/flt/bin_num.rb
ADDED
@@ -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
|