flt 1.2.1 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +10 -0
- data/Manifest.txt +5 -0
- data/README.txt +16 -10
- data/lib/flt/bin_num.rb +4 -0
- data/lib/flt/complex.rb +312 -0
- data/lib/flt/dec_num.rb +0 -21
- data/lib/flt/math.rb +47 -567
- data/lib/flt/num.rb +249 -20
- data/lib/flt/trigonometry.rb +746 -0
- data/lib/flt/version.rb +2 -2
- data/test/generate_trig_data.rb +169 -0
- data/test/test_basic.rb +5 -0
- metadata +11 -4
data/History.txt
CHANGED
data/Manifest.txt
CHANGED
@@ -9,6 +9,8 @@ lib/flt/num.rb
|
|
9
9
|
lib/flt/support.rb
|
10
10
|
lib/flt/dec_num.rb
|
11
11
|
lib/flt/math.rb
|
12
|
+
lib/flt/trigonometry.rb
|
13
|
+
lib/flt/complex.rb
|
12
14
|
lib/flt/bin_num.rb
|
13
15
|
lib/flt/float.rb
|
14
16
|
lib/flt/bigdecimal.rb
|
@@ -18,6 +20,8 @@ lib/flt/d.rb
|
|
18
20
|
lib/flt/b.rb
|
19
21
|
lib/flt/sugar.rb
|
20
22
|
setup.rb
|
23
|
+
test/generate_trig_data.rb
|
24
|
+
test/reader.rb
|
21
25
|
test/all_tests.rb
|
22
26
|
test/helper.rb
|
23
27
|
test/test_basic.rb
|
@@ -38,5 +42,6 @@ test/test_bin.rb
|
|
38
42
|
test/test_binfloat_conversion.rb
|
39
43
|
test/test_bin_arithmetic.rb
|
40
44
|
test/test_tol.rb
|
45
|
+
test/test_trig.rb
|
41
46
|
test/reader.rb
|
42
47
|
test/test_num_constructor.rb
|
data/README.txt
CHANGED
@@ -10,8 +10,8 @@ The Flt::Tolerance classes and the Flt.Tolerance() constructor handle floating p
|
|
10
10
|
tolerances defined in flexible ways.
|
11
11
|
|
12
12
|
Context classes are define in the files
|
13
|
-
flt/float.rb[link:
|
14
|
-
flt/bigdecimal.rb[link:
|
13
|
+
flt/float.rb[link:lib/flt/float_rb.html] and
|
14
|
+
flt/bigdecimal.rb[link:lib/flt/bigdecimal_rb.html]
|
15
15
|
for Float and BigDecimal numbers that aid to the interchangeability of floating point types. This
|
16
16
|
represent the only definition of identifiers outside the Flt namespace: the methods
|
17
17
|
Float.context() and BigDecimal.context() and some contants in Float.
|
@@ -375,7 +375,7 @@ This file defines +D+ as a synonym for +DecNum+:
|
|
375
375
|
puts +D('1.234') # -> 1.23
|
376
376
|
|
377
377
|
Some convenient methods are added to numeric classes by requiring the optional
|
378
|
-
flt/sugar.rb[link:
|
378
|
+
flt/sugar.rb[link:lib/flt/sugar_rb.html]. This must be explicitely required
|
379
379
|
because it could cause conflicts with other extensions of these classes.
|
380
380
|
|
381
381
|
require 'flt/sugar'
|
@@ -401,6 +401,12 @@ decimal point):
|
|
401
401
|
puts 100_000.000_001 # -> 100000.000001
|
402
402
|
puts 100_000.000_001.class # -> Float
|
403
403
|
|
404
|
+
There's also one important caveat with this notation: negative numbers with a zero integral part must be
|
405
|
+
parenthesed (otherwise the minus has no effect because it affects only the null integer part):
|
406
|
+
|
407
|
+
puts -0._5 # -> 0.5
|
408
|
+
puts -(0._5) # -> -0.5
|
409
|
+
|
404
410
|
== Error analysis
|
405
411
|
|
406
412
|
The DecNum#ulp() method returns the value of a "unit in the last place" for a given number under
|
@@ -502,21 +508,21 @@ Note also that if we normalize a value we will change it's precision to that of
|
|
502
508
|
|
503
509
|
There are two mathematical functions modules analogous to Ruby's Math for Float,
|
504
510
|
Flt::DecNum::Math and Flt::BinNum::Math.
|
505
|
-
Currently they consist of basic trigonometric functions, including hypot, and the
|
506
|
-
constants e and pi.
|
511
|
+
Currently they consist of basic trigonometric functions, including hypot, logarithms and the exponential
|
512
|
+
function, and the constants e and pi.
|
507
513
|
|
508
514
|
Its functions can be accessed in a number of ways:
|
509
515
|
|
510
516
|
require 'flt/math'
|
511
517
|
DecNum.context(:precision=>10) do |context|
|
512
|
-
# As module functions:
|
518
|
+
# As module functions, using the current context for the enclosing Num class:
|
513
519
|
puts DecNum::Math.sin(1)*DecNum::Math.pi # -> 2.643559064
|
514
520
|
# As functions of a context object:
|
515
521
|
puts context.sin(1)*context.pi # -> 2.643559064
|
516
522
|
# Through a math block:
|
517
523
|
puts DecNum.context.math{sin(1)*pi} # -> 2.643559064
|
518
524
|
puts DecNum.math{sin(1)*pi} # -> 2.643559064
|
519
|
-
# And can be included to be used
|
525
|
+
# And can be *included* to be used as private instance methods:
|
520
526
|
include DecNum::Math
|
521
527
|
puts sin(1)*pi # -> 2.643559064
|
522
528
|
end
|
@@ -527,10 +533,11 @@ Its functions can be accessed in a number of ways:
|
|
527
533
|
* Binary Floating point type: see the base Flt::Num class and the Flt::BinNum class
|
528
534
|
* Floating Point Contexts: see documentation for classes Flt::Num::ContextBase,
|
529
535
|
Flt::DecNum::Context and Flt::BinNum::Context
|
530
|
-
* Floating Point Tolerance: see the flt/tolerance.rb[link:
|
536
|
+
* Floating Point Tolerance: see the flt/tolerance.rb[link:lib/flt/tolerance_rb.html] file
|
531
537
|
and the Flt::Tolerance class
|
532
538
|
* Constructors: see Flt.DecNum(), Flt.BinNum() and Flt.Tolerance().
|
533
|
-
*
|
539
|
+
* Trigonometry functions: see Flt::Trigonometry.
|
540
|
+
* Complex number support: see the flt/complex.rb[link:lib/flt/complex_rb.html] file
|
534
541
|
|
535
542
|
= DecNum vs BigDecimal
|
536
543
|
|
@@ -592,7 +599,6 @@ EXPAND+
|
|
592
599
|
= Roadmap
|
593
600
|
|
594
601
|
* Trigonometry optimizations
|
595
|
-
* Complex support.
|
596
602
|
* Implement the missing GDA functions:
|
597
603
|
rotate, shift, trim, and, or, xor, invert,
|
598
604
|
max, min, maxmag, minmag, comparetotal, comparetotmag
|
data/lib/flt/bin_num.rb
CHANGED
data/lib/flt/complex.rb
ADDED
@@ -0,0 +1,312 @@
|
|
1
|
+
# Complex number support for Flt::Num types.
|
2
|
+
#
|
3
|
+
# Complex is extended to handle properly components of Num types.
|
4
|
+
#
|
5
|
+
# Examples:
|
6
|
+
# require 'flt/complex'
|
7
|
+
# include Flt
|
8
|
+
# DecNum.context(:precision=>4) do
|
9
|
+
# puts (Complex(1,2)*DecNum(2)).abs.inspect # => DecNum('4.472')
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# Complex functions are provided through Num.ccontext object, Context#.cmath blocks and CMath modules.
|
13
|
+
#
|
14
|
+
# Examples:
|
15
|
+
# require 'flt/complex'
|
16
|
+
# include Flt
|
17
|
+
# DecNum.context.precision = 10
|
18
|
+
# puts DecNum.ccontext.sqrt(-2) # => 0+1.414213562i
|
19
|
+
# puts DecNum.ccontext(:precision=>5).sqrt(-2) # => 0+1.4142i
|
20
|
+
# puts DecNum.context(:precision=>6).cmath{sqrt(-2)} # => 0+1.41421i
|
21
|
+
#
|
22
|
+
# CMath Examples:
|
23
|
+
# DecNum.context(:precision=>5) do
|
24
|
+
# puts DecNum::CMath.sqrt(-2) # => 0+1.4142i
|
25
|
+
# end
|
26
|
+
# DecNum.context.precision = 10
|
27
|
+
# include DecNum::CMath
|
28
|
+
# puts sqrt(-2) # => 0+1.414213562i
|
29
|
+
#
|
30
|
+
|
31
|
+
require 'flt/math'
|
32
|
+
require 'complex'
|
33
|
+
|
34
|
+
class Complex
|
35
|
+
|
36
|
+
alias abs! abs
|
37
|
+
def abs
|
38
|
+
num_class.nil? ? abs! : num_class.context.hypot(real, imag)
|
39
|
+
end
|
40
|
+
|
41
|
+
alias polar! polar
|
42
|
+
def polar
|
43
|
+
num_class.nil? ? polar! : [num_class.context.hypot(real, imag), num_class.context.atan2(imag, real)]
|
44
|
+
end
|
45
|
+
|
46
|
+
# alias power! **
|
47
|
+
# def **(other)
|
48
|
+
# if classnum_class_num.nil? && other.class_num.nil?
|
49
|
+
# self.power!(other)
|
50
|
+
# else
|
51
|
+
# num_class.ccontext.power(self, other)
|
52
|
+
# end
|
53
|
+
# end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def num_class
|
58
|
+
real.kind_of?(Flt::Num) ? real.class : imag.kind_of?(Flt::Num) ? imag.class : nil
|
59
|
+
end
|
60
|
+
|
61
|
+
end # Complex
|
62
|
+
|
63
|
+
module Flt
|
64
|
+
|
65
|
+
class ComplexContext # consider: < Num::ContextBase
|
66
|
+
|
67
|
+
def initialize(context)
|
68
|
+
@context = context
|
69
|
+
end
|
70
|
+
|
71
|
+
def num_class
|
72
|
+
@context.num_class
|
73
|
+
end
|
74
|
+
|
75
|
+
def math
|
76
|
+
num_class.context
|
77
|
+
end
|
78
|
+
|
79
|
+
def Num(z)
|
80
|
+
if z.kind_of?(Complex)
|
81
|
+
Complex.rectangular(*z.rectangular.map{|v| @context.num_class[v]})
|
82
|
+
else
|
83
|
+
Complex.rectangular(@context.num_class[z])
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def abs(z)
|
88
|
+
z.abs
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.math_function(mth, negative_arg_to_complex=false, extra_prec=3, &blk)
|
92
|
+
class_eval do
|
93
|
+
define_method mth do |*args|
|
94
|
+
is_complex = args.detect{|z| z.kind_of?(Complex)}
|
95
|
+
if is_complex || (negative_arg_to_complex && args.first<0)
|
96
|
+
num_class.context(:extra_precision=>extra_prec) do |mth|
|
97
|
+
#Complex.rectangular *blk[mth, *args].map{|v| @context.plus(v)}
|
98
|
+
Complex.rectangular *instance_exec(mth,*args,&blk).map{|v| @context.plus(v)}
|
99
|
+
end
|
100
|
+
else
|
101
|
+
@context.send(mth, *args) # Num(@context.send(mth, *args)) ?
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
math_function :exp do |mth, z|
|
108
|
+
re_exp = mth.exp(z.real)
|
109
|
+
[re_exp * mth.cos(z.imag), re_exp * mth.sin(z.imag)]
|
110
|
+
end
|
111
|
+
|
112
|
+
math_function :log, true do |mth, x, *args| # we can do |mth, x, b=nil| in Ruby 1.9 but not in 1.8
|
113
|
+
b = args.first
|
114
|
+
r, theta = Num(x).polar
|
115
|
+
[mth.log(r, b), theta]
|
116
|
+
end
|
117
|
+
|
118
|
+
math_function :ln, true do |mth, x|
|
119
|
+
r, theta = Num(x).polar
|
120
|
+
[mth.ln(r), theta]
|
121
|
+
end
|
122
|
+
|
123
|
+
math_function :log2, true do |mth, x|
|
124
|
+
r, theta = Num(x).polar
|
125
|
+
[mth.log2(r), theta]
|
126
|
+
end
|
127
|
+
|
128
|
+
math_function :log10, true do |mth, x|
|
129
|
+
r, theta = Num(x).polar
|
130
|
+
[mth.log10(r), theta]
|
131
|
+
end
|
132
|
+
|
133
|
+
math_function :power, true, 4 do |mth, x, y|
|
134
|
+
if (y.respond_to?(:zero?) && y.zero?) || y==0
|
135
|
+
[1,0]
|
136
|
+
else
|
137
|
+
r, theta = Num(x).polar
|
138
|
+
ore, oim = y.kind_of?(Complex) ? y.rectangular : [y, 0]
|
139
|
+
log_r = mth.ln(r)
|
140
|
+
nr = mth.exp(ore*log_r - oim*theta)
|
141
|
+
ntheta = theta*ore + oim*log_r
|
142
|
+
[nr*mth.cos(ntheta), nr*mth.sin(ntheta)]
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def sqrt(z)
|
147
|
+
z_is_complex = z.kind_of?(Complex)
|
148
|
+
if z_is_complex || z<0
|
149
|
+
if z_is_complex
|
150
|
+
re = im = nil
|
151
|
+
num_class.context(:extra_precision=>3) do |mth|
|
152
|
+
z = Num(z)
|
153
|
+
i_sign = z.imag.sign
|
154
|
+
r = abs(z)
|
155
|
+
x = z.real
|
156
|
+
re = ((r+x)/2).sqrt
|
157
|
+
im = ((r-x)/2).sqrt.copy_sign(i_sign)
|
158
|
+
end
|
159
|
+
fix_rect(re, im)
|
160
|
+
else
|
161
|
+
Complex(0, @context.sqrt(-z))
|
162
|
+
end
|
163
|
+
else
|
164
|
+
@context.sqrt(z)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
math_function :sin do |mth, z|
|
169
|
+
[mth.sin(z.real)*mth.cosh(z.imag), mth.cos(z.real)*mth.sinh(z.imag)]
|
170
|
+
end
|
171
|
+
|
172
|
+
math_function :cos do |mth, z|
|
173
|
+
[mth.cos(z.real)*mth.cosh(z.imag), -mth.sin(z.real)*mth.sinh(z.imag)]
|
174
|
+
end
|
175
|
+
|
176
|
+
math_function :tan do |mth, z|
|
177
|
+
mth.cmath{sin(z)/cos(z)}.rectangular
|
178
|
+
end
|
179
|
+
|
180
|
+
math_function :atan do |mth, z|
|
181
|
+
i = Complex(0,mth.num_class[1])
|
182
|
+
mth.cmath{i*ln((i+z)/(i-z))*num_class.one_half}.rectangular
|
183
|
+
end
|
184
|
+
|
185
|
+
math_function :sinh do |mth, z|
|
186
|
+
[mth.sinh(z.real)*mth.cos(z.imag), mth.cosh(z.real)*mth.sin(z.imag)]
|
187
|
+
end
|
188
|
+
|
189
|
+
math_function :cosh do |mth, z|
|
190
|
+
[mth.cosh(z.real)*mth.cos(z.imag), mth.sinh(z.real)*mth.sin(z.imag)]
|
191
|
+
end
|
192
|
+
|
193
|
+
math_function :tanh do |mth, z|
|
194
|
+
mth.cmath{sinh(z)/cosh(z)}.rectangular
|
195
|
+
end
|
196
|
+
|
197
|
+
math_function :asinh do |mth, z|
|
198
|
+
mth.cmath{ln(z+sqrt(z*z+1))}.rectangular
|
199
|
+
end
|
200
|
+
|
201
|
+
def asin(z)
|
202
|
+
z_is_complex = z.kind_of?(Complex)
|
203
|
+
if z_is_complex || z.abs>1
|
204
|
+
# z = Complex(1) unless z_is_complex
|
205
|
+
i = Complex(0,@context.num_class[1])
|
206
|
+
fix num_class.context(:extra_precision=>3).cmath {
|
207
|
+
-i*ln(i*z + sqrt(1-z*z))
|
208
|
+
}
|
209
|
+
else
|
210
|
+
@context.asin(z)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def acos(z)
|
215
|
+
z_is_complex = z.kind_of?(Complex)
|
216
|
+
if z_is_complex || z.abs>1
|
217
|
+
# z = Complex(1) unless z_is_complex
|
218
|
+
i = Complex(0,@context.num_class[1])
|
219
|
+
fix num_class.context(:extra_precision=>3).cmath {
|
220
|
+
-i*ln(z + i*sqrt(1-z*z))
|
221
|
+
}
|
222
|
+
else
|
223
|
+
@context.acos(z)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def acosh(z)
|
228
|
+
z_is_complex = z.kind_of?(Complex)
|
229
|
+
if z_is_complex || z<=1
|
230
|
+
# z = Complex(1) unless z_is_complex
|
231
|
+
fix num_class.context(:extra_precision=>3).cmath{ ln(z + sqrt(z*z-1)) }
|
232
|
+
else
|
233
|
+
@context.acosh(z)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def atanh(z)
|
238
|
+
z_is_complex = z.kind_of?(Complex)
|
239
|
+
if z_is_complex || z.abs>1
|
240
|
+
# z = Complex(1) unless z_is_complex
|
241
|
+
i = Complex(0,@context.num_class[1])
|
242
|
+
fix num_class.context(:extra_precision=>3).cmath{ num_class.one_half*ln((1+z)/(1-z)) }
|
243
|
+
else
|
244
|
+
@context.atanh(z)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
extend Forwardable
|
249
|
+
def_delegators :@context, :pi
|
250
|
+
|
251
|
+
|
252
|
+
private
|
253
|
+
|
254
|
+
def fix_rect(re, im)
|
255
|
+
Complex(@context.plus(re), @context.plus(im))
|
256
|
+
end
|
257
|
+
|
258
|
+
def fix_polar(r, theta)
|
259
|
+
num_class.context(:extra_precision=>3) do |mth|
|
260
|
+
re = r*mth.cos(theta)
|
261
|
+
im = r*mth.sin(theta)
|
262
|
+
end
|
263
|
+
fix_rect(re, im)
|
264
|
+
end
|
265
|
+
|
266
|
+
def fix(z)
|
267
|
+
fix_rect *z.rectangular
|
268
|
+
end
|
269
|
+
|
270
|
+
end # ComplexContext
|
271
|
+
|
272
|
+
class Num
|
273
|
+
|
274
|
+
def self.ccontext(*args)
|
275
|
+
ComplexContext(self.context(*args))
|
276
|
+
end
|
277
|
+
|
278
|
+
class ContextBase
|
279
|
+
def cmath(*parameters, &blk)
|
280
|
+
# if ComplexContext is derived from ContextBase: return ComplexContext(self).math(*parameters, &blk)
|
281
|
+
num_class.context(self) do
|
282
|
+
if parameters.empty?
|
283
|
+
Flt.ComplexContext(num_class.context).instance_eval &blk
|
284
|
+
else
|
285
|
+
Flt.xiComplexContext(num_class.context).instance_exec *parameters, &blk
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
end # Num
|
292
|
+
|
293
|
+
module_function
|
294
|
+
def ComplexContext(context)
|
295
|
+
ComplexContext.new(context)
|
296
|
+
end
|
297
|
+
|
298
|
+
module DecNum::CMath
|
299
|
+
include MathBase
|
300
|
+
num_class(DecNum){num_class.ccontext}
|
301
|
+
math_function *Trigonometry.public_instance_methods
|
302
|
+
math_function :exp, :log, :log2, :log10, :sqrt
|
303
|
+
end
|
304
|
+
|
305
|
+
module BinNum::CMath
|
306
|
+
include MathBase
|
307
|
+
num_class(BinNum){num_class.ccontext}
|
308
|
+
math_function *Trigonometry.public_instance_methods
|
309
|
+
math_function :exp, :log, :log2, :log10, :sqrt
|
310
|
+
end
|
311
|
+
|
312
|
+
end # Flt
|
data/lib/flt/dec_num.rb
CHANGED
@@ -44,27 +44,6 @@ class DecNum < Num
|
|
44
44
|
def initialize(*options)
|
45
45
|
super(DecNum, *options)
|
46
46
|
end
|
47
|
-
|
48
|
-
# Power. See DecNum#power()
|
49
|
-
def power(x,y,modulo=nil)
|
50
|
-
_convert(x).power(y,modulo,self)
|
51
|
-
end
|
52
|
-
|
53
|
-
# Returns the base 10 logarithm
|
54
|
-
def log10(x)
|
55
|
-
_convert(x).log10(self)
|
56
|
-
end
|
57
|
-
|
58
|
-
# Exponential function: e**x
|
59
|
-
def exp(x)
|
60
|
-
_convert(x).exp(self)
|
61
|
-
end
|
62
|
-
|
63
|
-
# Returns the natural (base e) logarithm
|
64
|
-
def ln(x)
|
65
|
-
_convert(x).ln(self)
|
66
|
-
end
|
67
|
-
|
68
47
|
end
|
69
48
|
|
70
49
|
# the DefaultContext is the base for new contexts; it can be changed.
|