1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package org.apache.commons.numbers.gamma;
24
25 import org.apache.commons.numbers.core.DD;
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 final class BoostErf {
44
45 private static final double ONE_OVER_ROOT_PI = 0.5641895835477562869480794515607725858;
46
47
48 private static final double ERFCX_APPROX = 6.71e7;
49
50
51
52 private static final double COMPUTE_ERF = 0.5;
53
54
55 private static final double ERFCX_NEG_X_MAX = Math.sqrt(Math.log(Double.MAX_VALUE / 2));
56
57
58
59
60 private static final double EXP_XX_1 = 0x1.5p-27;
61
62
63 private BoostErf() {
64
65 }
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107 static double erfc(double x) {
108 return erfImp(x, true, false);
109 }
110
111
112
113
114
115
116
117 static double erf(double x) {
118 return erfImp(x, false, false);
119 }
120
121
122
123
124
125
126
127
128
129
130
131
132
133 private static double erfImp(double z, boolean invert, boolean scaled) {
134 if (Double.isNaN(z)) {
135 return Double.NaN;
136 }
137
138 if (z < 0) {
139
140 if (!invert) {
141 return -erfImp(-z, invert, false);
142 } else if (z < -0.5) {
143 return 2 - erfImp(-z, invert, false);
144 } else {
145 return 1 + erfImp(-z, false, false);
146 }
147 }
148
149 double result;
150
151
152
153
154
155
156 if (z < COMPUTE_ERF) {
157
158
159
160
161 if (z < 1e-10) {
162 if (z == 0) {
163 result = z;
164 } else {
165 final double c = 0.003379167095512573896158903121545171688;
166 result = z * 1.125f + z * c;
167 }
168 } else {
169
170
171
172
173
174 final double Y = 1.044948577880859375f;
175 final double zz = z * z;
176 double P;
177 P = -0.000322780120964605683831;
178 P = -0.00772758345802133288487 + P * zz;
179 P = -0.0509990735146777432841 + P * zz;
180 P = -0.338165134459360935041 + P * zz;
181 P = 0.0834305892146531832907 + P * zz;
182 double Q;
183 Q = 0.000370900071787748000569;
184 Q = 0.00858571925074406212772 + Q * zz;
185 Q = 0.0875222600142252549554 + Q * zz;
186 Q = 0.455004033050794024546 + Q * zz;
187 Q = 1.0 + Q * zz;
188 result = z * (Y + P / Q);
189 }
190
191
192 } else if (scaled || (invert ? (z < 27.300781f) : (z < 5.9306640625f))) {
193
194
195
196
197 invert = !invert;
198 if (z < 1.5f) {
199
200
201
202
203 final double Y = 0.405935764312744140625f;
204 final double zm = z - 0.5;
205 double P;
206 P = 0.00180424538297014223957;
207 P = 0.0195049001251218801359 + P * zm;
208 P = 0.0888900368967884466578 + P * zm;
209 P = 0.191003695796775433986 + P * zm;
210 P = 0.178114665841120341155 + P * zm;
211 P = -0.098090592216281240205 + P * zm;
212 double Q;
213 Q = 0.337511472483094676155e-5;
214 Q = 0.0113385233577001411017 + Q * zm;
215 Q = 0.12385097467900864233 + Q * zm;
216 Q = 0.578052804889902404909 + Q * zm;
217 Q = 1.42628004845511324508 + Q * zm;
218 Q = 1.84759070983002217845 + Q * zm;
219 Q = 1.0 + Q * zm;
220 result = Y + P / Q;
221 if (scaled) {
222 result /= z;
223 } else {
224 result *= expmxx(z) / z;
225 }
226 } else if (z < 2.5f) {
227
228
229
230
231 final double Y = 0.50672817230224609375f;
232 final double zm = z - 1.5;
233 double P;
234 P = 0.000235839115596880717416;
235 P = 0.00323962406290842133584 + P * zm;
236 P = 0.0175679436311802092299 + P * zm;
237 P = 0.04394818964209516296 + P * zm;
238 P = 0.0386540375035707201728 + P * zm;
239 P = -0.0243500476207698441272 + P * zm;
240 double Q;
241 Q = 0.00410369723978904575884;
242 Q = 0.0563921837420478160373 + Q * zm;
243 Q = 0.325732924782444448493 + Q * zm;
244 Q = 0.982403709157920235114 + Q * zm;
245 Q = 1.53991494948552447182 + Q * zm;
246 Q = 1.0 + Q * zm;
247 result = Y + P / Q;
248 if (scaled) {
249 result /= z;
250 } else {
251 result *= expmxx(z) / z;
252 }
253
254
255 } else if (z < 4.0f) {
256
257
258
259
260 final double Y = 0.5405750274658203125f;
261 final double zm = z - 3.5;
262 double P;
263 P = 0.113212406648847561139e-4;
264 P = 0.000250269961544794627958 + P * zm;
265 P = 0.00212825620914618649141 + P * zm;
266 P = 0.00840807615555585383007 + P * zm;
267 P = 0.0137384425896355332126 + P * zm;
268 P = 0.00295276716530971662634 + P * zm;
269 double Q;
270 Q = 0.000479411269521714493907;
271 Q = 0.0105982906484876531489 + Q * zm;
272 Q = 0.0958492726301061423444 + Q * zm;
273 Q = 0.442597659481563127003 + Q * zm;
274 Q = 1.04217814166938418171 + Q * zm;
275 Q = 1.0 + Q * zm;
276 result = Y + P / Q;
277 if (scaled) {
278 result /= z;
279 } else {
280 result *= expmxx(z) / z;
281 }
282 } else {
283
284
285
286
287
288
289
290
291
292
293
294
295
296 final double izz = 1 / (z * z);
297 double p;
298 p = 1.63153871373020978498e-2;
299 p = 3.05326634961232344035e-1 + p * izz;
300 p = 3.60344899949804439429e-1 + p * izz;
301 p = 1.25781726111229246204e-1 + p * izz;
302 p = 1.60837851487422766278e-2 + p * izz;
303 p = 6.58749161529837803157e-4 + p * izz;
304 double q;
305 q = 1;
306 q = 2.56852019228982242072e00 + q * izz;
307 q = 1.87295284992346047209e00 + q * izz;
308 q = 5.27905102951428412248e-1 + q * izz;
309 q = 6.05183413124413191178e-2 + q * izz;
310 q = 2.33520497626869185443e-3 + q * izz;
311
312 result = izz * p / q;
313 result = (ONE_OVER_ROOT_PI - result) / z;
314
315 if (!scaled) {
316
317
318 result *= expmxx(z);
319 }
320 }
321 } else {
322
323
324
325 result = 0;
326 invert = !invert;
327 }
328
329 if (invert) {
330
331
332
333 result = 1 - result;
334 }
335
336 return result;
337 }
338
339
340
341
342
343
344
345
346
347
348 static double erfcx(double x) {
349 if (Double.isNaN(x)) {
350 return Double.NaN;
351 }
352
353
354 final double ax = Math.abs(x);
355 if (ax < COMPUTE_ERF) {
356
357
358
359 final double erfx = erf(x);
360 if (ax < EXP_XX_1) {
361
362 return 1 - erfx;
363 }
364
365
366
367
368
369
370
371
372
373
374
375
376
377 final double em1 = Math.expm1(x * x);
378 return -erfx * em1 + em1 - erfx + 1;
379 }
380
381
382 if (x < 0) {
383
384
385 if (x < -ERFCX_NEG_X_MAX) {
386
387 return Double.POSITIVE_INFINITY;
388 }
389
390 final double e = expxx(x);
391 return e - erfImp(-x, true, true) + e;
392 }
393
394
395 if (x > ERFCX_APPROX) {
396 return ONE_OVER_ROOT_PI / x;
397 }
398
399
400 return erfImp(x, true, true);
401 }
402
403
404
405
406
407
408
409 static double erfcInv(double z) {
410
411
412
413 if (z < 0 || z > 2 || Double.isNaN(z)) {
414
415 return Double.NaN;
416 }
417
418
419
420 if (z == (int) z) {
421
422
423
424
425 return z == 1 ? 0 : (1 - z) * Double.POSITIVE_INFINITY;
426 }
427
428
429
430
431
432
433 final double p;
434 final double q;
435 final double s;
436 if (z > 1) {
437 q = 2 - z;
438 p = 1 - q;
439 s = -1;
440 } else {
441 p = 1 - z;
442 q = z;
443 s = 1;
444 }
445
446
447
448
449 return s * erfInvImp(p, q);
450 }
451
452
453
454
455
456
457
458 static double erfInv(double z) {
459
460
461
462 if (z < -1 || z > 1 || Double.isNaN(z)) {
463
464 return Double.NaN;
465 }
466
467
468
469 if (z == (int) z) {
470
471
472
473
474
475 return z == 0 ? z : z * Double.POSITIVE_INFINITY;
476 }
477
478
479
480
481
482
483 final double p;
484 final double q;
485 final double s;
486 if (z < 0) {
487 p = -z;
488 q = 1 - p;
489 s = -1;
490 } else {
491 p = z;
492 q = 1 - z;
493 s = 1;
494 }
495
496
497
498 return s * erfInvImp(p, q);
499 }
500
501
502
503
504
505
506
507
508 private static double erfInvImp(double p, double q) {
509 final double result;
510
511 if (p <= 0.5) {
512
513
514
515
516
517
518
519
520
521
522
523
524 final float Y = 0.0891314744949340820313f;
525 double P;
526 P = -0.00538772965071242932965;
527 P = 0.00822687874676915743155 + P * p;
528 P = 0.0219878681111168899165 + P * p;
529 P = -0.0365637971411762664006 + P * p;
530 P = -0.0126926147662974029034 + P * p;
531 P = 0.0334806625409744615033 + P * p;
532 P = -0.00836874819741736770379 + P * p;
533 P = -0.000508781949658280665617 + P * p;
534 double Q;
535 Q = 0.000886216390456424707504;
536 Q = -0.00233393759374190016776 + Q * p;
537 Q = 0.0795283687341571680018 + Q * p;
538 Q = -0.0527396382340099713954 + Q * p;
539 Q = -0.71228902341542847553 + Q * p;
540 Q = 0.662328840472002992063 + Q * p;
541 Q = 1.56221558398423026363 + Q * p;
542 Q = -1.56574558234175846809 + Q * p;
543 Q = -0.970005043303290640362 + Q * p;
544 Q = 1.0 + Q * p;
545 final double g = p * (p + 10);
546 final double r = P / Q;
547 result = g * Y + g * r;
548 } else if (q >= 0.25) {
549
550
551
552
553
554
555
556
557
558
559
560
561 final float Y = 2.249481201171875f;
562 final double xs = q - 0.25f;
563 double P;
564 P = -3.67192254707729348546;
565 P = 21.1294655448340526258 + P * xs;
566 P = 17.445385985570866523 + P * xs;
567 P = -44.6382324441786960818 + P * xs;
568 P = -18.8510648058714251895 + P * xs;
569 P = 17.6447298408374015486 + P * xs;
570 P = 8.37050328343119927838 + P * xs;
571 P = 0.105264680699391713268 + P * xs;
572 P = -0.202433508355938759655 + P * xs;
573 double Q;
574 Q = 1.72114765761200282724;
575 Q = -22.6436933413139721736 + Q * xs;
576 Q = 10.8268667355460159008 + Q * xs;
577 Q = 48.5609213108739935468 + Q * xs;
578 Q = -20.1432634680485188801 + Q * xs;
579 Q = -28.6608180499800029974 + Q * xs;
580 Q = 3.9713437953343869095 + Q * xs;
581 Q = 6.24264124854247537712 + Q * xs;
582 Q = 1.0 + Q * xs;
583 final double g = Math.sqrt(-2 * Math.log(q));
584 final double r = P / Q;
585 result = g / (Y + r);
586 } else {
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607 final double x = Math.sqrt(-Math.log(q));
608 if (x < 3) {
609
610 final float Y = 0.807220458984375f;
611 final double xs = x - 1.125f;
612 double P;
613 P = -0.681149956853776992068e-9;
614 P = 0.285225331782217055858e-7 + P * xs;
615 P = -0.679465575181126350155e-6 + P * xs;
616 P = 0.00214558995388805277169 + P * xs;
617 P = 0.0290157910005329060432 + P * xs;
618 P = 0.142869534408157156766 + P * xs;
619 P = 0.337785538912035898924 + P * xs;
620 P = 0.387079738972604337464 + P * xs;
621 P = 0.117030156341995252019 + P * xs;
622 P = -0.163794047193317060787 + P * xs;
623 P = -0.131102781679951906451 + P * xs;
624 double Q;
625 Q = 0.01105924229346489121;
626 Q = 0.152264338295331783612 + Q * xs;
627 Q = 0.848854343457902036425 + Q * xs;
628 Q = 2.59301921623620271374 + Q * xs;
629 Q = 4.77846592945843778382 + Q * xs;
630 Q = 5.38168345707006855425 + Q * xs;
631 Q = 3.46625407242567245975 + Q * xs;
632 Q = 1.0 + Q * xs;
633 final double R = P / Q;
634 result = Y * x + R * x;
635 } else if (x < 6) {
636
637 final float Y = 0.93995571136474609375f;
638 final double xs = x - 3;
639 double P;
640 P = 0.266339227425782031962e-11;
641 P = -0.230404776911882601748e-9 + P * xs;
642 P = 0.460469890584317994083e-5 + P * xs;
643 P = 0.000157544617424960554631 + P * xs;
644 P = 0.00187123492819559223345 + P * xs;
645 P = 0.00950804701325919603619 + P * xs;
646 P = 0.0185573306514231072324 + P * xs;
647 P = -0.00222426529213447927281 + P * xs;
648 P = -0.0350353787183177984712 + P * xs;
649 double Q;
650 Q = 0.764675292302794483503e-4;
651 Q = 0.00263861676657015992959 + Q * xs;
652 Q = 0.0341589143670947727934 + Q * xs;
653 Q = 0.220091105764131249824 + Q * xs;
654 Q = 0.762059164553623404043 + Q * xs;
655 Q = 1.3653349817554063097 + Q * xs;
656 Q = 1.0 + Q * xs;
657 final double R = P / Q;
658 result = Y * x + R * x;
659 } else if (x < 18) {
660
661 final float Y = 0.98362827301025390625f;
662 final double xs = x - 6;
663 double P;
664 P = 0.99055709973310326855e-16;
665 P = -0.281128735628831791805e-13 + P * xs;
666 P = 0.462596163522878599135e-8 + P * xs;
667 P = 0.449696789927706453732e-6 + P * xs;
668 P = 0.149624783758342370182e-4 + P * xs;
669 P = 0.000209386317487588078668 + P * xs;
670 P = 0.00105628862152492910091 + P * xs;
671 P = -0.00112951438745580278863 + P * xs;
672 P = -0.0167431005076633737133 + P * xs;
673 double Q;
674 Q = 0.282243172016108031869e-6;
675 Q = 0.275335474764726041141e-4 + Q * xs;
676 Q = 0.000964011807005165528527 + Q * xs;
677 Q = 0.0160746087093676504695 + Q * xs;
678 Q = 0.138151865749083321638 + Q * xs;
679 Q = 0.591429344886417493481 + Q * xs;
680 Q = 1.0 + Q * xs;
681 final double R = P / Q;
682 result = Y * x + R * x;
683 } else {
684
685
686 final float Y = 0.99714565277099609375f;
687 final double xs = x - 18;
688 double P;
689 P = -0.116765012397184275695e-17;
690 P = 0.145596286718675035587e-11 + P * xs;
691 P = 0.411632831190944208473e-9 + P * xs;
692 P = 0.396341011304801168516e-7 + P * xs;
693 P = 0.162397777342510920873e-5 + P * xs;
694 P = 0.254723037413027451751e-4 + P * xs;
695 P = -0.779190719229053954292e-5 + P * xs;
696 P = -0.0024978212791898131227 + P * xs;
697 double Q;
698 Q = 0.509761276599778486139e-9;
699 Q = 0.144437756628144157666e-6 + Q * xs;
700 Q = 0.145007359818232637924e-4 + Q * xs;
701 Q = 0.000690538265622684595676 + Q * xs;
702 Q = 0.0169410838120975906478 + Q * xs;
703 Q = 0.207123112214422517181 + Q * xs;
704 Q = 1.0 + Q * xs;
705 final double R = P / Q;
706 result = Y * x + R * x;
707 }
708 }
709 return result;
710 }
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727 static double expxx(double x) {
728
729
730
731
732 final DD x2 = DD.ofSquare(x);
733 return expxx(x2.hi(), x2.lo());
734 }
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750 static double expmxx(double x) {
751 final DD x2 = DD.ofSquare(x);
752 return expxx(-x2.hi(), -x2.lo());
753 }
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772 private static double expxx(double a, double b) {
773
774
775
776
777
778
779
780
781
782
783
784 final double ea = Math.exp(a);
785
786 return ea * b + ea;
787 }
788 }