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/lib/flt/num.rb
CHANGED
@@ -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()
|
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
|
-
#
|
1193
|
-
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
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*
|
2353
|
-
# Here we use it for self.abs < context.minimum_normal*
|
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*
|
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
|
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
|
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
|
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 -
|
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
|
-
|
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
|
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
|
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
|
-
|
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
|