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