flt 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -595,6 +595,7 @@ class Num < Numeric
595
595
  @clamp = options[:clamp ] unless options[:clamp ].nil?
596
596
  @exact = options[:exact ] unless options[:exact ].nil?
597
597
  @angle = options[:angle ] unless options[:angle ].nil?
598
+ @precision += options[:extra_precision] unless options[:extra_precision].nil?
598
599
  update_precision
599
600
  end
600
601
  end
@@ -676,6 +677,36 @@ class Num < Numeric
676
677
  _convert(x)._neg(self)
677
678
  end
678
679
 
680
+ # Power. See DecNum#power()
681
+ def power(x,y,modulo=nil)
682
+ _convert(x).power(y,modulo,self)
683
+ end
684
+
685
+ # Returns the base 10 logarithm
686
+ def log10(x)
687
+ _convert(x).log10(self)
688
+ end
689
+
690
+ # Returns the base 2 logarithm
691
+ def log2(x)
692
+ _convert(x).log10(self)
693
+ end
694
+
695
+ # Exponential function: e**x
696
+ def exp(x)
697
+ _convert(x).exp(self)
698
+ end
699
+
700
+ # Returns the natural (base e) logarithm
701
+ def ln(x)
702
+ _convert(x).ln(self)
703
+ end
704
+
705
+ # Ruby-style log function: arbitrary base logarithm which defaults to natural logarithm
706
+ def log(x, base=nil)
707
+ _convert(x).log(base, self)
708
+ end
709
+
679
710
  # Converts a number to a string
680
711
  def to_string(x, eng=false)
681
712
  _convert(x)._fix(self).to_s(eng, self)
@@ -1176,8 +1207,8 @@ class Num < Numeric
1176
1207
  private :define_context
1177
1208
 
1178
1209
  # The current context (thread-local).
1179
- # If arguments are passed they are interpreted as in Num.define_context() to change
1180
- # the current context.
1210
+ # If arguments are passed they are interpreted as in Num.define_context() and an altered copy
1211
+ # of the current context is returned.
1181
1212
  # If a block is given, this method is a synonym for Num.local_context().
1182
1213
  def self.context(*args, &blk)
1183
1214
  if blk
@@ -1189,11 +1220,12 @@ class Num < Numeric
1189
1220
  self._context = ctxt = self::DefaultContext.dup if ctxt.nil?
1190
1221
  ctxt
1191
1222
  else
1192
- # change the current context
1193
- # TODO: consider doing self._context = ... here
1194
- # so we would have DecNum.context = c that assigns a duplicate of c
1195
- # and DecNum.context c to set alias c
1196
- self.context = define_context(*args)
1223
+ # Return a modified copy of the current context
1224
+ if args.first.kind_of?(ContextBase)
1225
+ self.define_context(*args)
1226
+ else
1227
+ self.define_context(self.context, *args)
1228
+ end
1197
1229
  end
1198
1230
  end
1199
1231
 
@@ -1202,6 +1234,11 @@ class Num < Numeric
1202
1234
  self._context = c.dup
1203
1235
  end
1204
1236
 
1237
+ # Modify the current context, e.g. DecNum.set_context(:precision=>10)
1238
+ def self.set_context(*args)
1239
+ self.context = define_context(*args)
1240
+ end
1241
+
1205
1242
  # Defines a scope with a local context. A context can be passed which will be
1206
1243
  # set a the current context for the scope; also a hash can be passed with
1207
1244
  # options to apply to the local scope.
@@ -1262,6 +1299,11 @@ class Num < Numeric
1262
1299
  new [+1, nil, :nan]
1263
1300
  end
1264
1301
 
1302
+ # One half: 1/2
1303
+ def one_half
1304
+ new '0.5'
1305
+ end
1306
+
1265
1307
  def int_radix_power(n)
1266
1308
  self.radix**n
1267
1309
  end
@@ -2280,6 +2322,164 @@ class Num < Numeric
2280
2322
 
2281
2323
  end
2282
2324
 
2325
+ # Naive implementation of exponential and logarithm functions; should be replaced
2326
+ # by something more efficient in specific Num classes.
2327
+
2328
+ # Exponential function
2329
+ def exp(context=nil)
2330
+ context = num_class.define_context(context)
2331
+
2332
+ # exp(NaN) = NaN
2333
+ ans = _check_nans(context)
2334
+ return ans if ans
2335
+
2336
+ # exp(-Infinity) = 0
2337
+ return num_class.zero if self.infinite? && (self.sign == -1)
2338
+
2339
+ # exp(0) = 1
2340
+ return Num(1) if self.zero?
2341
+
2342
+ # exp(Infinity) = Infinity
2343
+ return Num(self) if self.infinite?
2344
+
2345
+ # the result is now guaranteed to be inexact (the true
2346
+ # mathematical result is transcendental). There's no need to
2347
+ # raise Rounded and Inexact here---they'll always be raised as
2348
+ # a result of the call to _fix.
2349
+ return context.exception(Inexact, 'Inexact exp') if context.exact?
2350
+ p = context.precision
2351
+ adj = self.adjusted_exponent
2352
+
2353
+ if self.sign == +1 and adj > _number_of_digits((context.emax+1)*3)
2354
+ # overflow
2355
+ ans = Num(+1, 1, context.emax+1)
2356
+ elsif self.sign == -1 and adj > _number_of_digits((-context.etiny+1)*3)
2357
+ # underflow to 0
2358
+ ans = Num(+1, 1, context.etiny-1)
2359
+ elsif self.sign == +1 and adj < -p
2360
+ # p+1 digits; final round will raise correct flags
2361
+ ans = Num(+1, num_clas.int_radix_power(p)+1, -p)
2362
+ elsif self.sign == -1 and adj < -p-1
2363
+ # p+1 digits; final round will raise correct flags
2364
+ ans = Num(+1, num_clas.int_radix_power(p+1)-1, -p-1)
2365
+ else
2366
+ # general case
2367
+ x_sign = self.sign
2368
+ x = self.copy_sign(+1)
2369
+ i, lasts, s, fact, num = 0, 0, 1, 1, 1
2370
+ elim = [context.emax, -context.emin, 10000].max
2371
+ xprec = num_class.radix==10 ? 3 : 4
2372
+ num_class.local_context(context, :extra_precision=>xprec, :rounding=>:half_even, :elimit=>elim) do
2373
+ while s != lasts
2374
+ lasts = s
2375
+ i += 1
2376
+ fact *= i
2377
+ num *= x
2378
+ s += num / fact
2379
+ end
2380
+ s = num_class.Num(1)/s if x_sign<0
2381
+ end
2382
+ ans = s
2383
+ end
2384
+
2385
+ # at this stage, ans should round correctly with *any*
2386
+ # rounding mode, not just with ROUND_HALF_EVEN
2387
+ num_class.context(context, :rounding=>:half_even) do |local_context|
2388
+ ans = ans._fix(local_context)
2389
+ context.flags = local_context.flags
2390
+ end
2391
+
2392
+ return ans
2393
+ end
2394
+
2395
+ # Returns the natural (base e) logarithm
2396
+ def ln(context=nil)
2397
+ context = num_class.define_context(context)
2398
+
2399
+ # ln(NaN) = NaN
2400
+ ans = _check_nans(context)
2401
+ return ans if ans
2402
+
2403
+ # ln(0.0) == -Infinity
2404
+ return num_class.infinity(-1) if self.zero?
2405
+
2406
+ # ln(Infinity) = Infinity
2407
+ return num_class.infinity if self.infinite? && self.sign == +1
2408
+
2409
+ # ln(1.0) == 0.0
2410
+ return num_class.zero if self == Num(1)
2411
+
2412
+ # ln(negative) raises InvalidOperation
2413
+ return context.exception(InvalidOperation, 'ln of a negative value') if self.sign==-1
2414
+
2415
+ # result is irrational, so necessarily inexact
2416
+ return context.exception(Inexact, 'Inexact exp') if context.exact?
2417
+
2418
+ elim = [context.emax, -context.emin, 10000].max
2419
+ xprec = num_class.radix==10 ? 3 : 4
2420
+ num_class.local_context(context, :extra_precision=>xprec, :rounding=>:half_even, :elimit=>elim) do
2421
+
2422
+ one = num_class.Num(1)
2423
+
2424
+ x = self
2425
+ if (expo = x.adjusted_exponent)<-1 || expo>=2
2426
+ x = x.scaleb(-expo)
2427
+ else
2428
+ expo = nil
2429
+ end
2430
+
2431
+ x = (x-one)/(x+one)
2432
+ x2 = x*x
2433
+ ans = x
2434
+ d = ans
2435
+ i = one
2436
+ last_ans = nil
2437
+ while ans != last_ans
2438
+ last_ans = ans
2439
+ x = x2*x
2440
+ i += 2
2441
+ d = x/i
2442
+ ans += d
2443
+ end
2444
+ ans *= 2
2445
+ if expo
2446
+ ans += num_class.Num(num_class.radix).ln*expo
2447
+ end
2448
+ end
2449
+
2450
+ num_class.context(context, :rounding=>:half_even) do |local_context|
2451
+ ans = ans._fix(local_context)
2452
+ context.flags = local_context.flags
2453
+ end
2454
+ return ans
2455
+ end
2456
+
2457
+ # Ruby-style logarithm of arbitrary base, e (natural base) by default
2458
+ def log(b=nil, context=nil)
2459
+ if b.nil?
2460
+ self.ln(context)
2461
+ elsif b==10
2462
+ self.log10(context)
2463
+ elsif b==2
2464
+ self.log2(context)
2465
+ else
2466
+ context = num_class.define_context(context)
2467
+ +num_class.context(:extra_precision=>3){self.ln(context)/num_class[b].ln(context)}
2468
+ end
2469
+ end
2470
+
2471
+ # Returns the base 10 logarithm
2472
+ def log10(context=nil)
2473
+ context = num_class.define_context(context)
2474
+ num_class.context(:extra_precision=>3){self.ln/num_class.Num(10).ln}
2475
+ end
2476
+
2477
+ # Returns the base 2 logarithm
2478
+ def log2(context=nil)
2479
+ context = num_class.define_context(context)
2480
+ num_class.context(context, :extra_precision=>3){self.ln()/num_class.Num(2).ln}
2481
+ end
2482
+
2283
2483
  # Convert to other numerical type.
2284
2484
  def convert_to(type, context=nil)
2285
2485
  context = define_context(context)
@@ -2349,14 +2549,14 @@ class Num < Numeric
2349
2549
  # The ulp here is context.maximum_finite - context.maximum_finite.next_minus
2350
2550
  return Num(+1, 1, context.etop)
2351
2551
  elsif self.zero? || self.adjusted_exponent <= context.emin
2352
- # This is the ulp value for self.abs <= context.minimum_normal*DecNum.context
2353
- # Here we use it for self.abs < context.minimum_normal*DecNum.context;
2552
+ # This is the ulp value for self.abs <= context.minimum_normal*num_class.context
2553
+ # Here we use it for self.abs < context.minimum_normal*num_class.context;
2354
2554
  # because of the simple exponent check; the remaining cases are handled below.
2355
2555
  return context.minimum_nonzero
2356
2556
  else
2357
2557
  # The next can compute the ulp value for the values that
2358
2558
  # self.abs > context.minimum_normal && self.abs <= context.maximum_finite
2359
- # The cases self.abs < context.minimum_normal*DecNum.context have been handled above.
2559
+ # The cases self.abs < context.minimum_normal*num_class.context have been handled above.
2360
2560
 
2361
2561
  # assert self.normal? && self.abs>context.minimum_nonzero
2362
2562
  norm = self.normalize
@@ -2452,7 +2652,7 @@ class Num < Numeric
2452
2652
  reduce.split == other.reduce.split
2453
2653
  end
2454
2654
 
2455
- # Compares like <=> but returns a DecNum value.
2655
+ # Compares like <=> but returns a Num value.
2456
2656
  def compare(other, context=nil)
2457
2657
 
2458
2658
  other = _convert(other)
@@ -2475,7 +2675,7 @@ class Num < Numeric
2475
2675
  end
2476
2676
  end
2477
2677
 
2478
- # Synonym for DecNum#adjusted_exponent()
2678
+ # Synonym for Num#adjusted_exponent()
2479
2679
  def scientific_exponent
2480
2680
  adjusted_exponent
2481
2681
  end
@@ -2718,7 +2918,7 @@ class Num < Numeric
2718
2918
  # digit to be rounded (exponent == -places)
2719
2919
  # * :precision or :significan_digits is the number of digits
2720
2920
  # * :power 10^exponent, value of the digit to be rounded,
2721
- # should be passed as a type convertible to DecNum.
2921
+ # should be passed as a type convertible to Num.
2722
2922
  # * :index 0-based index of the digit to be rounded
2723
2923
  # * :rindex right 0-based index of the digit to be rounded
2724
2924
  #
@@ -2743,7 +2943,7 @@ class Num < Numeric
2743
2943
  elsif v=(opt[:exponent])
2744
2944
  prec = adjusted_exponent + 1 - v
2745
2945
  elsif v=(opt[:power])
2746
- prec = adjusted_exponent + 1 - DecNum(v).adjusted_exponent
2946
+ prec = adjusted_exponent + 1 - num_class.Num(v).adjusted_exponent
2747
2947
  elsif v=(opt[:index])
2748
2948
  prec = i+1
2749
2949
  elsif v=(opt[:rindex])
@@ -2869,13 +3069,31 @@ class Num < Numeric
2869
3069
  format(context, options.merge(:eng=>eng))
2870
3070
  end
2871
3071
 
2872
- # Raises to the power of x.
3072
+ # Raises to the power of x, to modulo if given.
2873
3073
  #
2874
- # If self is negative then other
3074
+ # With two arguments, compute self**other. If self is negative then other
2875
3075
  # must be integral. The result will be inexact unless other is
2876
3076
  # integral and the result is finite and can be expressed exactly
2877
3077
  # in 'precision' digits.
2878
- def power(other, context=nil)
3078
+ #
3079
+ # With three arguments, compute (self**other) % modulo. For the
3080
+ # three argument form, the following restrictions on the
3081
+ # arguments hold:
3082
+ #
3083
+ # - all three arguments must be integral
3084
+ # - other must be nonnegative
3085
+ # - at least one of self or other must be nonzero
3086
+ # - modulo must be nonzero and have at most 'precision' digits
3087
+ #
3088
+ # The result of a.power(b, modulo) is identical to the result
3089
+ # that would be obtained by computing (a**b) % modulo with
3090
+ # unbounded precision, but may be computed more efficiently. It is
3091
+ # always exact.
3092
+ def power(other, modulo=nil, context=nil)
3093
+ if context.nil? && (modulo.kind_of?(ContextBase) || modulo.is_a?(Hash))
3094
+ context = modulo
3095
+ modulo = nil
3096
+ end
2879
3097
 
2880
3098
  context = num_class.define_context(context)
2881
3099
  other = _convert(other)
@@ -3045,6 +3263,9 @@ class Num < Numeric
3045
3263
  end
3046
3264
  context.exception Underflow if ans.adjusted_exponent < context.emin
3047
3265
  end
3266
+
3267
+ ans = ans % modulo if modulo
3268
+
3048
3269
  # unlike exp, ln and log10, the power function respects the
3049
3270
  # rounding mode; no need to use ROUND_HALF_EVEN here
3050
3271
  ans._fix(context)
@@ -3264,7 +3485,7 @@ class Num < Numeric
3264
3485
 
3265
3486
  if number_of_digits > max_payload_len
3266
3487
  payload = payload.to_s[-max_payload_len..-1].to_i
3267
- return DecNum([@sign, payload, @exp])
3488
+ return num_class.Num([@sign, payload, @exp])
3268
3489
  end
3269
3490
  end
3270
3491
  Num(self)
@@ -3421,7 +3642,7 @@ class Num < Numeric
3421
3642
  leftdigits = dec_pos
3422
3643
  end
3423
3644
 
3424
- # TODO: DRY (this code is duplicated in DecNum#format)
3645
+ # TODO: DRY (this code is duplicated in num_class#format)
3425
3646
  if exp<=0 && leftdigits>-6
3426
3647
  dotplace = leftdigits
3427
3648
  elsif !eng
@@ -4122,7 +4343,15 @@ class Num < Numeric
4122
4343
  #
4123
4344
  # If the base does not correspond to one of the predefined classes (DecNum, BinNum), a new class
4124
4345
  # is dynamically generated.
4125
- def [](base)
4346
+ #
4347
+ # The [] operator can also be applied to classes derived from Num to act as a constructor
4348
+ # (short hand for .new):
4349
+ # Flt::Num[10]['0.1'] # same as FLt::DecNum['0.1'] or Flt.DecNum('0.1') or Flt::DecNum.new('0.1')
4350
+ def [](*args)
4351
+ return self.Num(*args) if self!=Num # && self.ancestors.include?(Num)
4352
+ raise RuntimeError, "Invalid number of arguments (#{args.size}) for Num.[]; 1 expected." unless args.size==1
4353
+ base = args.first
4354
+
4126
4355
  case base
4127
4356
  when 10
4128
4357
  DecNum
@@ -0,0 +1,746 @@
1
+ require 'flt/dec_num'
2
+
3
+ module Flt
4
+
5
+ # Trigonometry functions. The angular units used by these functions can be specified
6
+ # with the +angle+ attribute of the context. The accepted values are:
7
+ # * :rad for radians
8
+ # * :deg for degrees
9
+ # * :grad for gradians
10
+ #
11
+ # These functions are injected in Context objects.
12
+ module Trigonometry
13
+
14
+ # Cosine of an angle given in the units specified by the context +angle+ attribute.
15
+ def cos(x)
16
+ cos_base(num_class[x])
17
+ end
18
+
19
+ # Sine of an angle given in the units specified by the context +angle+ attribute.
20
+ def sin(x)
21
+ sin_base(num_class[x])
22
+ end
23
+
24
+ # Tangent of an angle given in the units specified by the context +angle+ attribute.
25
+ def tan(x)
26
+ tan_base(num_class[x])
27
+ end
28
+
29
+ # Arc-tangent. The result is in the units specified by the context +angle+ attribute.
30
+ # If the angular units are radians the result is in [-pi/2, pi/2]; it is in [-90,90] in degrees.
31
+ def atan(x)
32
+ atan_base(num_class[x])
33
+ end
34
+
35
+ # Arc-tangent with two arguments (principal value of the argument of the complex number x+i*y).
36
+ # The result is in the units specified by the context +angle+ attribute.
37
+ # If the angular units are radians the result is in [-pi, pi]; it is in [-180,180] in degrees.
38
+ def atan2(y, x)
39
+ atan2_base(num_class[y], num_class[x])
40
+ end
41
+
42
+ # Arc-sine. The result is in the units specified by the context +angle+ attribute.
43
+ # If the angular units are radians the result is in [-pi/2, pi/2]; it is in [-90,90] in degrees.
44
+ def asin(x)
45
+ asin_base(num_class[x])
46
+ end
47
+
48
+ # Arc-cosine. The result is in the units specified by the context +angle+ attribute.
49
+ # If the angular units are radians the result is in [-pi/2, pi/2]; it is in [-90,90] in degrees.
50
+ def acos(x)
51
+ acos_base(num_class[x])
52
+ end
53
+
54
+ # Length of the hypotenuse of a right-angle triangle (modulus or absolute value of the complex x+i*y).
55
+ def hypot(x, y)
56
+ hypot_base(num_class[x], num_class[y])
57
+ end
58
+
59
+ # Pi
60
+ def pi(round_digits=nil)
61
+ round_digits ||= self.precision
62
+ if Trigonometry.pi_digits < round_digits
63
+ # provisional implementation (very slow)
64
+ lasts = 0
65
+ t, s, n, na, d, da = Trigonometry.pi_cache
66
+ num_class.context(self) do |local_context|
67
+ local_context.precision = round_digits + 6
68
+ tol = Rational(1,num_class.int_radix_power(local_context.precision+1))
69
+ while (s-lasts)>tol
70
+ lasts = s
71
+ n, na = n+na, na+8
72
+ d, da = d+da, da+32
73
+ t = (t * n) / d
74
+ s += t
75
+ end
76
+ Trigonometry.pi_value = num_class[s]
77
+ Trigonometry.pi_digits = round_digits
78
+ Trigonometry.pi_cache = [t, s, n, na, d, da]
79
+ end
80
+ end
81
+ num_class.context(self, :precision=>round_digits){+Trigonometry.pi_value}
82
+ end
83
+
84
+ def e(digits=nil)
85
+ num_class.context(self) do |local_context|
86
+ local_context.precision = digits if digits
87
+ num_class.Num(1).exp
88
+ end
89
+ end
90
+
91
+ def half
92
+ @half ||= num_class.one_half
93
+ end
94
+
95
+ # Hyperbolic sine
96
+ def sinh(x)
97
+ sinh_base(num_class[x])
98
+ end
99
+
100
+ # Hyperbolic cosine
101
+ def cosh(x)
102
+ cosh_base(num_class[x])
103
+ end
104
+
105
+ # Hyperbolic tangent
106
+ def tanh(x)
107
+ tanh_base(num_class[x])
108
+ end
109
+
110
+ # Hyperbolic arcsine
111
+ def asinh(x)
112
+ asinh_base(num_class[x])
113
+ end
114
+
115
+ # Hyperbolic arccosine
116
+ def acosh(x)
117
+ acosh_base(num_class[x])
118
+ end
119
+
120
+ # Hyperbolic arctangent
121
+ def atanh(x)
122
+ atanh_base(num_class[x])
123
+ end
124
+
125
+ protected
126
+
127
+ @pi_value = nil
128
+ @pi_digits = 0
129
+ @pi_cache = [Rational(3), Rational(3), 1, 0, 0, 24]
130
+ class <<self
131
+ attr_accessor :pi_value, :pi_digits, :pi_cache
132
+ end
133
+
134
+ def cos_base(x)
135
+ x = x.copy_sign(+1) # note that abs rounds; copy_sign does not.
136
+ rev_sign = false
137
+ s = nil
138
+ num_class.context(self) do |local_context|
139
+ local_context.precision += 3 # extra digits for intermediate steps
140
+ x,k,pi_2 = local_context.reduce_angle2(x,2)
141
+ rev_sign = true if k>1
142
+ if k % 2 == 0
143
+ x = pi_2 - x
144
+ else
145
+ rev_sign = !rev_sign
146
+ end
147
+ x = local_context.to_rad(x)
148
+ i, lasts, fact, num = 1, 0, 1, num_class[x]
149
+ s = num
150
+ x2 = -x*x
151
+ while s != lasts
152
+ lasts = s
153
+ i += 2
154
+ fact *= i * (i-1)
155
+ num *= x2
156
+ s += num / fact
157
+ end
158
+ end
159
+ return rev_sign ? minus(s) : plus(s)
160
+ end
161
+
162
+ def sin_base(x)
163
+ sign = x.sign
164
+ s = nil
165
+ num_class.context(self) do |local_context|
166
+ local_context.precision += 3 # extra digits for intermediate steps
167
+ x = x.copy_sign(+1) if sign<0
168
+ x,k,pi_2 = local_context.reduce_angle2(x,2)
169
+ sign = -sign if k>1
170
+ x = pi_2 - x if k % 2 == 1
171
+ x = local_context.to_rad(x)
172
+ i, lasts, fact, num = 1, 0, 1, num_class[x]
173
+ s = num
174
+ x2 = -x*x
175
+ while s != lasts
176
+ lasts = s
177
+ i += 2
178
+ fact *= i * (i-1)
179
+ num *= x2
180
+ s += num / fact
181
+ end
182
+ end
183
+ return plus(s).copy_sign(sign)
184
+ end
185
+
186
+ def tan_base(x)
187
+ plus(num_class.context(self) do |local_context|
188
+ local_context.precision += 2 # extra digits for intermediate steps
189
+ s,c = local_context.sin(x), local_context.cos(x)
190
+ s/c
191
+ end)
192
+ end
193
+
194
+ def atan_base(x)
195
+ s = nil
196
+ conversion = true
197
+ extra_prec = num_class.radix==2 ? 4 : 2
198
+ num_class.context(self) do |local_context|
199
+ local_context.precision += extra_prec
200
+ if x == 0
201
+ return num_class.zero
202
+ elsif x.abs > 1
203
+ if x.infinite?
204
+ s = local_context.quarter_cycle.copy_sign(x)
205
+ conversion = false
206
+ break
207
+ else
208
+ # c = (quarter_cycle).copy_sign(x)
209
+ c = (half*local_context.pi).copy_sign(x)
210
+ x = 1 / x
211
+ end
212
+ end
213
+ local_context.precision += extra_prec
214
+ x_squared = x ** 2
215
+ if x_squared.zero? || x_squared.subnormal?
216
+ s = x
217
+ s = c - s if c && c!=0
218
+ break
219
+ end
220
+ y = x_squared / (1 + x_squared)
221
+ y_over_x = y / x
222
+ i = num_class.zero; lasts = 0; s = y_over_x; coeff = 1; num = y_over_x
223
+ while s != lasts
224
+ lasts = s
225
+ i += 2
226
+ coeff *= i / (i + 1)
227
+ num *= y
228
+ s += coeff * num
229
+ end
230
+ if c && c!= 0
231
+ s = c - s
232
+ end
233
+ end
234
+ return conversion ? rad_to(s) : plus(s)
235
+ end
236
+
237
+ def atan2_base(y, x)
238
+ abs_y = y.abs
239
+ abs_x = x.abs
240
+ y_is_real = !x.infinite?
241
+
242
+ if x != 0
243
+ if y_is_real
244
+ a = y!=0 ? atan(y / x) : num_class.zero
245
+ a += half_cycle.copy_sign(y) if x < 0
246
+ return a
247
+ elsif abs_y == abs_x
248
+ one = num_class[1]
249
+ x = one.copy_sign(x)
250
+ y = one.copy_sign(y)
251
+ return half_cycle * (2 - x) / (4 * y)
252
+ end
253
+ end
254
+
255
+ if y != 0
256
+ return atan(num_class.infinity(y.sign))
257
+ elsif x < 0
258
+ return half_cycle.copy_sign(x)
259
+ else
260
+ return num_class.zero
261
+ end
262
+ end
263
+
264
+ def asin_base(x)
265
+ x = +x
266
+ return self.exception(Num::InvalidOperation, 'asin needs -1 <= x <= 1') if x.abs > 1
267
+
268
+ if x == -1
269
+ return -quarter_cycle
270
+ elsif x == 0
271
+ return num_class.zero
272
+ elsif x == 1
273
+ return quarter_cycle
274
+ end
275
+
276
+ num_class.context(self) do |local_context|
277
+ local_context.precision += 3
278
+ x = x/(1-x*x).sqrt
279
+ x = local_context.atan(x)
280
+ end
281
+ +x
282
+ end
283
+
284
+ def acos_base(x)
285
+
286
+ return self.exception(Num::InvalidOperation, 'acos needs -1 <= x <= 2') if x.abs > 1
287
+
288
+ if x == -1
289
+ return half_cycle
290
+ elsif x == 0
291
+ return quarter_cycle
292
+ elsif x == 1
293
+ return num_class.zero
294
+ end
295
+
296
+ required_precision = self.precision
297
+
298
+ if x < half
299
+ num_class.context(self, :precision=>required_precision+2) do
300
+ x = x/(1-x*x).sqrt
301
+ x = num_class.context.quarter_cycle - num_class.context.atan(x)
302
+ end
303
+ else
304
+ # valid for x>=0
305
+ num_class.context(self, :precision=>required_precision+3) do
306
+
307
+ # x = (1-x*x).sqrt # x*x may require double precision if x*x is near 1
308
+ x = (1-num_class.context(self, :precision=>required_precision*2){x*x}).sqrt
309
+
310
+ x = num_class.context.asin(x)
311
+ end
312
+ end
313
+ +x
314
+
315
+ end
316
+
317
+ def hypot_base(x, y)
318
+ +num_class.context(self) do |local_context|
319
+ local_context.precision += 3
320
+ (x*x + y*y).sqrt
321
+ end
322
+ end
323
+
324
+ def sinh_base(x)
325
+ sign = x.sign
326
+ s = nil
327
+ num_class.context(self) do |local_context|
328
+ local_context.precision += 3 # extra digits for intermediate steps
329
+ x = x.copy_sign(+1) if sign<0
330
+ if x > 1
331
+ s = half*(x.exp - (-x).exp)
332
+ else
333
+ i, lasts, fact, num = 1, 0, 1, num_class[x]
334
+ s = num
335
+ x2 = x*x
336
+ while s != lasts
337
+ lasts = s
338
+ i += 2
339
+ fact *= i * (i-1)
340
+ num *= x2
341
+ s += num / fact
342
+ end
343
+ end
344
+ end
345
+ return plus(s).copy_sign(sign)
346
+ end
347
+
348
+ def cosh_base(x)
349
+ s = nil
350
+ num_class.context(self) do |local_context|
351
+ local_context.precision += 3 # extra digits for intermediate steps
352
+ x = x.copy_sign(+1)
353
+ s = half*(x.exp + (-x).exp)
354
+ end
355
+ return plus(s)
356
+ end
357
+
358
+ def tanh_base(x)
359
+ s = nil
360
+ num_class.context(self) do |local_context|
361
+ local_context.precision += 3 # extra digits for intermediate steps
362
+ s = sinh_base(x)/cosh_base(x)
363
+ end
364
+ return plus(s)
365
+ end
366
+
367
+ def asinh_base(x)
368
+ sign = x.sign
369
+ x = x.copy_sign(+1)
370
+ s = nil
371
+
372
+ num_class.context(self) do |local_context|
373
+ x_squared = x ** 2
374
+ if x_squared.zero? || x_squared.subnormal?
375
+ s = x
376
+ else
377
+ # TODO: more accurate formula for small x: if x<...
378
+ if x.adjusted_exponent >= local_context.precision
379
+ s = local_context.ln(x+x)
380
+ else
381
+ s = local_context.ln(x + local_context.sqrt(x_squared + 1))
382
+ end
383
+ end
384
+ end
385
+ return plus(s).copy_sign(sign)
386
+ end
387
+
388
+ def acosh_base(x)
389
+
390
+ return self.exception(Num::InvalidOperation, 'acosh needs x >= 1') if x < 1
391
+
392
+ x = x.copy_sign(+1)
393
+ s = nil
394
+
395
+ num_class.context(self) do |local_context|
396
+ if x == 1
397
+ s = num_class.zero
398
+ else
399
+ if x.adjusted_exponent >= local_context.precision
400
+ s = x+x
401
+ else
402
+ s = x + local_context.sqrt((x+1)*(x-1))
403
+ end
404
+ s = local_context.ln(s)
405
+ end
406
+ end
407
+ return plus(s)
408
+ end
409
+
410
+ def atanh_base(x)
411
+ sign = x.sign
412
+ x = x.copy_sign(+1)
413
+ s = nil
414
+
415
+ return self.exception(Num::InvalidOperation, 'asinh needs -1 <= x <= 1') if x > 1
416
+
417
+ num_class.context(self) do |local_context|
418
+ if x.adjusted_exponent <= -local_context.precision
419
+ s = x
420
+ else
421
+ s = (1 + x) / (1 - x)
422
+ s = half*local_context.ln(s)
423
+ end
424
+ end
425
+ return plus(s).copy_sign(sign)
426
+ end
427
+
428
+ def pi2(decimals=nil)
429
+ num_class.context(self, :precision=>decimals) do |local_context|
430
+ local_context.pi*2
431
+ end
432
+ end
433
+
434
+ def invpi(decimals=nil)
435
+ num_class.context(self, :precision=>decimals) do |local_context|
436
+ num_class[1]/local_context.pi
437
+ end
438
+ end
439
+
440
+ def inv2pi(decimals=nil)
441
+ num_class.context(self, :precision=>decimals) do |local_context|
442
+ num_class.Num(1)/local_context.pi2
443
+ end
444
+ end
445
+
446
+ # class <<self
447
+ # private
448
+
449
+ def modtwopi(x)
450
+ return plus(num_class.context(self, :precision=>self.precision*3){x.modulo(one_cycle)})
451
+ end
452
+
453
+ # Reduce angle to [0,2Pi)
454
+ def reduce_angle(a)
455
+ modtwopi(a)
456
+ end
457
+
458
+ # Reduce angle to [0,Pi/k0) (result is not rounded to precision)
459
+ def reduce_angle2(a,k0=nil) # divisor of pi or nil for pi*2
460
+ # we could reduce first to pi*2 to avoid the mod k0 operation
461
+ k,r,divisor = num_class.context do
462
+ num_class.context.precision *= 3
463
+ m = k0.nil? ? one_cycle : half_cycle/k0
464
+ a.divmod(m)+[m]
465
+ end
466
+ [r, k.modulo(k0*2).to_i, divisor]
467
+ end
468
+
469
+ def one_cycle
470
+ case self.angle
471
+ when :rad
472
+ pi2
473
+ when :deg
474
+ num_class.Num(360)
475
+ when :grad
476
+ num_class.Num(400)
477
+ end
478
+ end
479
+
480
+ def half_cycle
481
+ case self.angle
482
+ when :rad
483
+ pi(num_class.context.precision)
484
+ when :deg
485
+ num_class.Num(180)
486
+ when :grad
487
+ num_class.Num(200)
488
+ end
489
+ end
490
+
491
+ def quarter_cycle
492
+ case self.angle
493
+ when :rad
494
+ half*pi(num_class.context.precision)
495
+ when :deg
496
+ num_class.Num(90)
497
+ when :grad
498
+ num_class.Num(100)
499
+ end
500
+ end
501
+
502
+ def to_rad(x)
503
+ case self.angle
504
+ when :rad
505
+ plus(x)
506
+ else
507
+ plus(num_class.context(self, :extra_precision=>3){|lc| x*lc.pi/half_cycle})
508
+ end
509
+ end
510
+
511
+ def to_deg(x)
512
+ case self.angle
513
+ when :deg
514
+ plus(x)
515
+ else
516
+ plus(num_class.context(self, :extra_precision=>3){x*num_class[180]/half_cycle})
517
+ end
518
+ end
519
+
520
+ def to_grad(x)
521
+ case self.angle
522
+ when :deg
523
+ plus(x)
524
+ else
525
+ plus(num_class.context(self, :extra_precision=>3){x*num_class[200]/half_cycle})
526
+ end
527
+ end
528
+
529
+ def to_angle(angular_units, x)
530
+ return plus(x) if angular_units == self.angle
531
+ case angular_units
532
+ when :rad
533
+ to_rad(x)
534
+ when :deg
535
+ to_deg(x)
536
+ when :grad
537
+ to_grad(x)
538
+ end
539
+ end
540
+
541
+ def rad_to(x)
542
+ case self.angle
543
+ when :rad
544
+ plus(x)
545
+ else
546
+ plus(num_class.context(self, :extra_precision=>3){|lc| x*half_cycle/lc.pi})
547
+ end
548
+ end
549
+
550
+ def deg_to(x)
551
+ case self.angle
552
+ when :deg
553
+ plus(x)
554
+ else
555
+ plus(num_class.context(self, :extra_precision=>3){x*half_cycle/num_class[180]})
556
+ end
557
+ end
558
+
559
+ def grad_to(x)
560
+ case self.angle
561
+ when :grad
562
+ plus(x)
563
+ else
564
+ plus(num_class.context(self, :extra_precision=>3){x*half_cycle/num_class[200]})
565
+ end
566
+ end
567
+
568
+ def angle_to(x, angular_units)
569
+ return plus(x) if angular_units == self.angle
570
+ case angular_units
571
+ when :rad
572
+ rad_to(x)
573
+ when :deg
574
+ deg_to(x)
575
+ when :grad
576
+ grad_to(x)
577
+ end
578
+ end
579
+
580
+ #end
581
+
582
+ module Support
583
+ module_function
584
+ def iarccot(x, unity)
585
+ xpow = unity / x
586
+ n = 1
587
+ sign = 1
588
+ sum = 0
589
+ loop do
590
+ term = xpow / n
591
+ break if term == 0
592
+ sum += sign * (xpow/n)
593
+ xpow /= x*x
594
+ n += 2
595
+ sign = -sign
596
+ end
597
+ sum
598
+ end
599
+ end
600
+
601
+ end # Trigonometry
602
+
603
+ Num::ContextBase.class_eval{include Trigonometry}
604
+
605
+ class DecNum
606
+
607
+ module Trigonometry
608
+
609
+ include Flt::Trigonometry::Support
610
+
611
+ # Pi
612
+ @pi_cache = nil # truncated pi digits as a string
613
+ @pi_cache_digits = 0
614
+ PI_MARGIN = 10
615
+ class <<self
616
+ attr_accessor :pi_cache, :pi_cache_digits
617
+ end
618
+
619
+ def pi(round_digits=nil)
620
+ round_digits ||= self.precision
621
+ digits = round_digits
622
+ if Trigonometry.pi_cache_digits <= digits # we need at least one more truncated digit
623
+ continue = true
624
+ while continue
625
+ margin = PI_MARGIN # margin to reduce recomputing with more digits to avoid ending in 0 or 5
626
+ digits += margin + 1
627
+ fudge = 10
628
+ unity = 10**(digits+fudge)
629
+ v = 4*(4*iarccot(5, unity) - iarccot(239, unity))
630
+ v = v.to_s[0,digits]
631
+ # if the last digit is 0 or 5 the truncated value may not be good for rounding
632
+ loop do
633
+ #last_digit = v%10
634
+ last_digit = v[-1,1].to_i
635
+ continue = (last_digit==5 || last_digit==0)
636
+ if continue && margin>0
637
+ # if we have margin we back-up one digit
638
+ margin -= 1
639
+ v = v[0...-1]
640
+ else
641
+ break
642
+ end
643
+ end
644
+ end
645
+ Trigonometry.pi_cache_digits = digits + margin - PI_MARGIN # @pi_cache.size
646
+ Trigonometry.pi_cache = v # DecNum(+1, v, 1-digits) # cache truncated value
647
+ end
648
+ # Now we avoid rounding too much because it is slow
649
+ l = round_digits + 1
650
+ while (l<Num[16]::Trigonometry.pi_cache_digits) && [0,5].include?(Trigonometry.pi_cache[l-1,1].to_i)
651
+ l += 1
652
+ end
653
+ v = Trigonometry.pi_cache[0,l]
654
+ num_class.context(self, :precision=>round_digits){+num_class.Num(+1,v.to_i,1-l)}
655
+ end
656
+
657
+ end # DecNum::Trigonometry
658
+
659
+ DecNum::Context.class_eval{include DecNum::Trigonometry}
660
+
661
+ end # DecNum
662
+
663
+ Num[16].class_eval do
664
+
665
+ module Num[16]::Trigonometry
666
+
667
+ extend Flt::Trigonometry::Support
668
+
669
+ # Pi
670
+ @pi_cache = nil # truncated pi digits as a string
671
+ @pi_cache_digits = 0
672
+ PI_MARGIN = 10
673
+ class <<self
674
+ attr_accessor :pi_cache, :pi_cache_digits
675
+ end
676
+
677
+ # truncated hex digits for rounding hexadecimally at round_digits
678
+ def self.pi_hex_digits(round_digits=nil)
679
+ round_digits ||= self.precision
680
+ digits = round_digits
681
+ if Num[16]::Trigonometry.pi_cache_digits <= digits # we need at least one more truncated digit
682
+ continue = true
683
+ while continue
684
+ margin = PI_MARGIN # margin to reduce recomputing with more digits to avoid ending in 0 or 5
685
+ digits += margin + 1
686
+ fudge = 16
687
+ unity = 16**(digits+fudge)
688
+ v = 4*(4*iarccot(5, unity) - iarccot(239, unity))
689
+ v = v.to_s(16)[0,digits]
690
+ # if the last digit is 0 or 8 the truncated value may not be good for rounding
691
+ loop do
692
+ #last_digit = v%16
693
+ last_digit = v[-1,1].to_i(16)
694
+ continue = (last_digit==8 || last_digit==0)
695
+ if continue && margin>0
696
+ # if we have margin we back-up one digit
697
+ margin -= 1
698
+ v = v[0...-1]
699
+ else
700
+ break
701
+ end
702
+ end
703
+ end
704
+ Num[16]::Trigonometry.pi_cache_digits = digits + margin - PI_MARGIN # @pi_cache.size
705
+ Num[16]::Trigonometry.pi_cache = v # DecNum(+1, v, 1-digits) # cache truncated value
706
+ end
707
+ # Now we avoid rounding too much because it is slow
708
+ l = round_digits + 1
709
+ while (l<Num[16]::Trigonometry.pi_cache_digits) && [0,8].include?(Num[16]::Trigonometry.pi_cache[l-1,1].to_i(16))
710
+ l += 1
711
+ end
712
+ Num[16]::Trigonometry.pi_cache[0,l]
713
+ end
714
+
715
+ def pi(round_digits=nil)
716
+ v = Num[16]::Trigonometry.pi_hex_digits(round_digits)
717
+ l = v.size
718
+ num_class.context(self, :precision=>round_digits){+num_class.Num(+1,v.to_i(16),1-l)}
719
+ end
720
+
721
+ end # Num[16]::Trigonometry
722
+
723
+ Num[16]::Context.class_eval{include Num[16]::Trigonometry}
724
+
725
+ end # Num[16]
726
+
727
+ class BinNum
728
+ module Trigonometry
729
+ def pi(round_digits=nil)
730
+ round_digits ||= self.precision
731
+ nhexd = (round_digits+3)/4 + 1
732
+ v = Num[16]::Trigonometry.pi_hex_digits(nhexd)
733
+ l = v.size
734
+ v = v.to_i(16)
735
+ e = (1-l)*4
736
+ # add trailing 01 for rounding (there always be some non null digit beyond the rounding point)
737
+ v <<= 2
738
+ v |= 1
739
+ e -= 2
740
+ num_class.context(self, :precision=>round_digits){+num_class.Num(+1,v,e)}
741
+ end
742
+ end
743
+ BinNum::Context.class_eval{include BinNum::Trigonometry}
744
+ end
745
+
746
+ end # Flt