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