@@ -4,40 +4,73 @@ using Base.Math: @horner
4
4
using Base. MPFR: ROUNDING_MODE
5
5
6
6
for f in (:erf , :erfc )
7
+ internalf = Symbol (:_ , f)
8
+ libopenlibmf = QuoteNode (f)
9
+ libopenlibmf0 = QuoteNode (Symbol (f, :f ))
10
+ openspecfunf = QuoteNode (Symbol (:Faddeeva_ , f))
11
+ mpfrf = QuoteNode (Symbol (:mpfr_ , f))
7
12
@eval begin
8
- ($ f)(x:: Float64 ) = ccall (($ (string (f)),libopenlibm), Float64, (Float64,), x)
9
- ($ f)(x:: Float32 ) = ccall (($ (string (f," f" )),libopenlibm), Float32, (Float32,), x)
10
- ($ f)(x:: Real ) = ($ f)(float (x))
11
- ($ f)(a:: Float16 ) = Float16 ($ f (Float32 (a)))
12
- ($ f)(a:: Complex{Float16} ) = Complex {Float16} ($ f (Complex {Float32} (a)))
13
- function ($ f)(x:: BigFloat )
13
+ $ f (x:: Number ) = $ internalf (float (x))
14
+
15
+ $ internalf (x:: Float64 ) = ccall (($ libopenlibmf, libopenlibm), Float64, (Float64,), x)
16
+ $ internalf (x:: Float32 ) = ccall (($ libopenlibmf0, libopenlibm), Float32, (Float32,), x)
17
+ $ internalf (x:: Float16 ) = Float16 ($ internalf (Float32 (x)))
18
+
19
+ $ internalf (z:: Complex{Float64} ) = Complex {Float64} (ccall (($ openspecfunf, libopenspecfun), Complex{Float64}, (Complex{Float64}, Float64), z, zero (Float64)))
20
+ $ internalf (z:: Complex{Float32} ) = Complex {Float32} (ccall (($ openspecfunf, libopenspecfun), Complex{Float64}, (Complex{Float64}, Float64), Complex {Float64} (z), Float64 (eps (Float32))))
21
+ $ internalf (z:: Complex{Float16} ) = Complex {Float16} ($ internalf (Complex {Float32} (z)))
22
+
23
+ function $internalf (x:: BigFloat )
14
24
z = BigFloat ()
15
- ccall (($ ( string ( :mpfr_ ,f)) , :libmpfr ), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, ROUNDING_MODE[])
25
+ ccall (($ mpfrf , :libmpfr ), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, ROUNDING_MODE[])
16
26
return z
17
27
end
18
- ($ f)(x:: AbstractFloat ) = error (" not implemented for " , typeof (x))
19
28
end
20
29
end
21
30
22
- for f in (:erf , :erfc , :erfcx , :erfi , :Dawson )
23
- fname = (f === :Dawson ) ? :dawson : f
31
+ for f in (:erfcx , :erfi , :dawson )
32
+ internalf = Symbol (:_ , f)
33
+ openspecfunfsym = Symbol (:Faddeeva_ , f === :dawson ? :Dawson : f)
34
+ openspecfunfF64 = QuoteNode (Symbol (openspecfunfsym, :_re ))
35
+ openspecfunfCF64 = QuoteNode (openspecfunfsym)
24
36
@eval begin
25
- ($ fname)(z:: Complex{Float64} ) = Complex {Float64} (ccall (($ (string (" Faddeeva_" ,f)),libopenspecfun), Complex{Float64}, (Complex{Float64}, Float64), z, zero (Float64)))
26
- ($ fname)(z:: Complex{Float32} ) = Complex {Float32} (ccall (($ (string (" Faddeeva_" ,f)),libopenspecfun), Complex{Float64}, (Complex{Float64}, Float64), Complex {Float64} (z), Float64 (eps (Float32))))
37
+ $ f (x:: Number ) = $ internalf (float (x))
38
+
39
+ $ internalf (x:: Float64 ) = ccall (($ openspecfunfF64, libopenspecfun), Float64, (Float64,), x)
40
+ $ internalf (x:: Float32 ) = Float32 ($ internalf (Float64 (x)))
41
+ $ internalf (x:: Float16 ) = Float16 ($ internalf (Float64 (x)))
27
42
28
- ($ fname)(z:: Complex ) = ($ fname)(float (z))
29
- ($ fname)(z:: Complex{<:AbstractFloat} ) = throw (MethodError ($ fname,(z,)))
43
+ $ internalf (z:: Complex{Float64} ) = Complex {Float64} (ccall (($ openspecfunfCF64, libopenspecfun), Complex{Float64}, (Complex{Float64}, Float64), z, zero (Float64)))
44
+ $ internalf (z:: Complex{Float32} ) = Complex {Float32} (ccall (($ openspecfunfCF64, libopenspecfun), Complex{Float64}, (Complex{Float64}, Float64), Complex {Float64} (z), Float64 (eps (Float32))))
45
+ $ internalf (z:: Complex{Float16} ) = Complex {Float16} ($ internalf (Complex {Float32} (z)))
30
46
end
31
47
end
32
48
33
- for f in (:erfcx , :erfi , :Dawson )
34
- fname = (f === :Dawson ) ? :dawson : f
35
- @eval begin
36
- ($ fname)(x:: Float64 ) = ccall (($ (string (" Faddeeva_" ,f," _re" )),libopenspecfun), Float64, (Float64,), x)
37
- ($ fname)(x:: Float32 ) = Float32 (ccall (($ (string (" Faddeeva_" ,f," _re" )),libopenspecfun), Float64, (Float64,), Float64 (x)))
38
-
39
- ($ fname)(x:: Real ) = ($ fname)(float (x))
40
- ($ fname)(x:: AbstractFloat ) = throw (MethodError ($ fname,(x,)))
49
+ # MPFR has an open TODO item for this function
50
+ # until then, we use [DLMF 7.12.1](https://dlmf.nist.gov/7.12.1) for the tail
51
+ function _erfcx (x:: BigFloat )
52
+ if x <= (Clong == Int32 ? 0x1 p15 : 0x1 p30)
53
+ # any larger gives internal overflow
54
+ return exp (x^ 2 )* erfc (x)
55
+ elseif ! isfinite (x)
56
+ return 1 / x
57
+ else
58
+ # asymptotic series
59
+ # starts to diverge at iteration i = 2^30 or 2^60
60
+ # final term will be < Γ(2*i+1)/(2^i * Γ(i+1)) / (2^(i+1))
61
+ # so good to (lgamma(2*i+1) - lgamma(i+1))/log(2) - 2*i - 1
62
+ # ≈ 3.07e10 or 6.75e19 bits
63
+ # which is larger than the memory of the respective machines
64
+ ϵ = eps (BigFloat)/ 4
65
+ v = 1 / (2 * x* x)
66
+ k = 1
67
+ s = w = - k* v
68
+ while abs (w) > ϵ
69
+ k += 2
70
+ w *= - k* v
71
+ s += w
72
+ end
73
+ return (1 + s)/ (x* sqrtπ)
41
74
end
42
75
end
43
76
@@ -204,7 +237,9 @@ Using the rational approximants tabulated in:
204
237
> <http://www.jstor.org/stable/2005402>
205
238
combined with Newton iterations for `BigFloat`.
206
239
"""
207
- function erfinv (x:: Float64 )
240
+ erfinv (x:: Real ) = _erfinv (float (x))
241
+
242
+ function _erfinv (x:: Float64 )
208
243
a = abs (x)
209
244
if a >= 1.0
210
245
if x == 1.0
@@ -272,7 +307,7 @@ function erfinv(x::Float64)
272
307
end
273
308
end
274
309
275
- function erfinv (x:: Float32 )
310
+ function _erfinv (x:: Float32 )
276
311
a = abs (x)
277
312
if a >= 1.0f0
278
313
if x == 1.0f0
@@ -315,7 +350,25 @@ function erfinv(x::Float32)
315
350
end
316
351
end
317
352
318
- erfinv (x:: Union{Integer,Rational} ) = erfinv (float (x))
353
+ function _erfinv (y:: BigFloat )
354
+ xfloat = erfinv (Float64 (y))
355
+ if isfinite (xfloat)
356
+ x = BigFloat (xfloat)
357
+ else
358
+ # Float64 overflowed, use asymptotic estimate instead
359
+ # from erfc(x) ≈ exp(-x²)/x√π ≈ y ⟹ -log(yπ) ≈ x² + log(x) ≈ x²
360
+ x = copysign (sqrt (- log ((1 - abs (y))* sqrtπ)), y)
361
+ isfinite (x) || return x
362
+ end
363
+ sqrtπhalf = sqrtπ * big (0.5 )
364
+ tol = 2 eps (abs (x))
365
+ while true # Newton iterations
366
+ Δx = sqrtπhalf * (erf (x) - y) * exp (x^ 2 )
367
+ x -= Δx
368
+ abs (Δx) < tol && break
369
+ end
370
+ return x
371
+ end
319
372
320
373
@doc raw """
321
374
erfcinv(x)
@@ -341,7 +394,9 @@ Using the rational approximants tabulated in:
341
394
> <http://www.jstor.org/stable/2005402>
342
395
combined with Newton iterations for `BigFloat`.
343
396
"""
344
- function erfcinv (y:: Float64 )
397
+ erfcinv (x:: Real ) = _erfcinv (float (x))
398
+
399
+ function _erfcinv (y:: Float64 )
345
400
if y > 0.0625
346
401
return erfinv (1.0 - y)
347
402
elseif y <= 0.0
@@ -393,7 +448,7 @@ function erfcinv(y::Float64)
393
448
end
394
449
end
395
450
396
- function erfcinv (y:: Float32 )
451
+ function _erfcinv (y:: Float32 )
397
452
if y > 0.0625f0
398
453
return erfinv (1.0f0 - y)
399
454
elseif y <= 0.0f0
@@ -415,27 +470,7 @@ function erfcinv(y::Float32)
415
470
end
416
471
end
417
472
418
- function erfinv (y:: BigFloat )
419
- xfloat = erfinv (Float64 (y))
420
- if isfinite (xfloat)
421
- x = BigFloat (xfloat)
422
- else
423
- # Float64 overflowed, use asymptotic estimate instead
424
- # from erfc(x) ≈ exp(-x²)/x√π ≈ y ⟹ -log(yπ) ≈ x² + log(x) ≈ x²
425
- x = copysign (sqrt (- log ((1 - abs (y))* sqrtπ)), y)
426
- isfinite (x) || return x
427
- end
428
- sqrtπhalf = sqrtπ * big (0.5 )
429
- tol = 2 eps (abs (x))
430
- while true # Newton iterations
431
- Δx = sqrtπhalf * (erf (x) - y) * exp (x^ 2 )
432
- x -= Δx
433
- abs (Δx) < tol && break
434
- end
435
- return x
436
- end
437
-
438
- function erfcinv (y:: BigFloat )
473
+ function _erfcinv (y:: BigFloat )
439
474
yfloat = Float64 (y)
440
475
xfloat = erfcinv (yfloat)
441
476
if isfinite (xfloat)
@@ -461,36 +496,6 @@ function erfcinv(y::BigFloat)
461
496
return x
462
497
end
463
498
464
- erfcinv (x:: Union{Integer,Rational} ) = erfcinv (float (x))
465
-
466
- # MPFR has an open TODO item for this function
467
- # until then, we use [DLMF 7.12.1](https://dlmf.nist.gov/7.12.1) for the tail
468
- function erfcx (x:: BigFloat )
469
- if x <= (Clong == Int32 ? 0x1 p15 : 0x1 p30)
470
- # any larger gives internal overflow
471
- return exp (x^ 2 )* erfc (x)
472
- elseif ! isfinite (x)
473
- return 1 / x
474
- else
475
- # asymptotic series
476
- # starts to diverge at iteration i = 2^30 or 2^60
477
- # final term will be < Γ(2*i+1)/(2^i * Γ(i+1)) / (2^(i+1))
478
- # so good to (lgamma(2*i+1) - lgamma(i+1))/log(2) - 2*i - 1
479
- # ≈ 3.07e10 or 6.75e19 bits
480
- # which is larger than the memory of the respective machines
481
- ϵ = eps (BigFloat)/ 4
482
- v = 1 / (2 * x* x)
483
- k = 1
484
- s = w = - k* v
485
- while abs (w) > ϵ
486
- k += 2
487
- w *= - k* v
488
- s += w
489
- end
490
- return (1 + s)/ (x* sqrtπ)
491
- end
492
- end
493
-
494
499
@doc raw """
495
500
logerfc(x)
496
501
@@ -511,7 +516,9 @@ See also: [`erfcx(x)`](@ref erfcx).
511
516
Based on the [`erfc(x)`](@ref erfc) and [`erfcx(x)`](@ref erfcx) functions.
512
517
Currently only implemented for `Float32`, `Float64`, and `BigFloat`.
513
518
"""
514
- function logerfc (x:: Union{Float32, Float64, BigFloat} )
519
+ logerfc (x:: Real ) = _logerfc (float (x))
520
+
521
+ function _logerfc (x:: Union{Float32, Float64, BigFloat} )
515
522
# Don't include Float16 in the Union, otherwise logerfc would currently work for x <= 0.0, but not x > 0.0
516
523
if x > 0.0
517
524
return log (erfcx (x)) - x^ 2
@@ -520,9 +527,6 @@ function logerfc(x::Union{Float32, Float64, BigFloat})
520
527
end
521
528
end
522
529
523
- logerfc (x:: Real ) = logerfc (float (x))
524
- logerfc (x:: AbstractFloat ) = throw (MethodError (logerfc, x))
525
-
526
530
@doc raw """
527
531
logerfcx(x)
528
532
@@ -543,7 +547,9 @@ See also: [`erfcx(x)`](@ref erfcx).
543
547
Based on the [`erfc(x)`](@ref erfc) and [`erfcx(x)`](@ref erfcx) functions.
544
548
Currently only implemented for `Float32`, `Float64`, and `BigFloat`.
545
549
"""
546
- function logerfcx (x:: Union{Float32, Float64, BigFloat} )
550
+ logerfcx (x:: Real ) = _logerfcx (float (x))
551
+
552
+ function _logerfcx (x:: Union{Float32, Float64, BigFloat} )
547
553
# Don't include Float16 in the Union, otherwise logerfc would currently work for x <= 0.0, but not x > 0.0
548
554
if x < 0.0
549
555
return log (erfc (x)) + x^ 2
@@ -552,9 +558,6 @@ function logerfcx(x::Union{Float32, Float64, BigFloat})
552
558
end
553
559
end
554
560
555
- logerfcx (x:: Real ) = logerfcx (float (x))
556
- logerfcx (x:: AbstractFloat ) = throw (MethodError (logerfcx, x))
557
-
558
561
@doc raw """
559
562
logerf(x, y)
560
563
0 commit comments