flt 1.2.1 → 1.3.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 +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.
|