flt 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,13 @@
1
+ == 1.3.0 2010-06-22
2
+
3
+ * New features
4
+ - Complex math
5
+ - Hyperbolic trigonometry
6
+
7
+ * Bugfixes
8
+ - Num.context use was counter-intuitive
9
+ - Various math corrections
10
+
1
11
  == 1.2.1 2010-06-16
2
12
 
3
13
  * New features
@@ -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:files/lib/flt/float_rb.html] and
14
- flt/bigdecimal.rb[link:files/lib/flt/bigdecimal_rb.html]
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:files/lib/flt/sugar_rb.html]. This must be explicitely required
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 ans private instance methods:
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:files/lib/flt/tolerance_rb.html] file
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
- * Mathematical functions: see Flt::MathBase.
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
@@ -11,6 +11,10 @@ class BinNum < Num
11
11
  2
12
12
  end
13
13
 
14
+ def one_half
15
+ new [+1, 1, -1]
16
+ end
17
+
14
18
  # Integral power of the base: radix**n for integer n; returns an integer.
15
19
  def int_radix_power(n)
16
20
  (n < 0) ? (2**n) : (1<<n)
@@ -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
@@ -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.