flt 1.2.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +10 -0
- data/Manifest.txt +5 -0
- data/README.txt +16 -10
- data/lib/flt/bin_num.rb +4 -0
- data/lib/flt/complex.rb +312 -0
- data/lib/flt/dec_num.rb +0 -21
- data/lib/flt/math.rb +47 -567
- data/lib/flt/num.rb +249 -20
- data/lib/flt/trigonometry.rb +746 -0
- data/lib/flt/version.rb +2 -2
- data/test/generate_trig_data.rb +169 -0
- data/test/test_basic.rb +5 -0
- metadata +11 -4
data/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
|