1const ERX: f64 = 8.45062911510467529297e-01;
101const EFX8: f64 = 1.02703333676410069053e+00;
102const PP0: f64 = 1.28379167095512558561e-01;
103const PP1: f64 = -3.25042107247001499370e-01;
104const PP2: f64 = -2.84817495755985104766e-02;
105const PP3: f64 = -5.77027029648944159157e-03;
106const PP4: f64 = -2.37630166566501626084e-05;
107const QQ1: f64 = 3.97917223959155352819e-01;
108const QQ2: f64 = 6.50222499887672944485e-02;
109const QQ3: f64 = 5.08130628187576562776e-03;
110const QQ4: f64 = 1.32494738004321644526e-04;
111const QQ5: f64 = -3.96022827877536812320e-06;
112
113const PA0: f64 = -2.36211856075265944077e-03;
114const PA1: f64 = 4.14856118683748331666e-01;
115const PA2: f64 = -3.72207876035701323847e-01;
116const PA3: f64 = 3.18346619901161753674e-01;
117const PA4: f64 = -1.10894694282396677476e-01;
118const PA5: f64 = 3.54783043256182359371e-02;
119const PA6: f64 = -2.16637559486879084300e-03;
120const QA1: f64 = 1.06420880400844228286e-01;
121const QA2: f64 = 5.40397917702171048937e-01;
122const QA3: f64 = 7.18286544141962662868e-02;
123const QA4: f64 = 1.26171219808761642112e-01;
124const QA5: f64 = 1.36370839120290507362e-02;
125const QA6: f64 = 1.19844998467991074170e-02;
126
127const RA0: f64 = -9.86494403484714822705e-03;
128const RA1: f64 = -6.93858572707181764372e-01;
129const RA2: f64 = -1.05586262253232909814e+01;
130const RA3: f64 = -6.23753324503260060396e+01;
131const RA4: f64 = -1.62396669462573470355e+02;
132const RA5: f64 = -1.84605092906711035994e+02;
133const RA6: f64 = -8.12874355063065934246e+01;
134const RA7: f64 = -9.81432934416914548592e+00;
135const SA1: f64 = 1.96512716674392571292e+01;
136const SA2: f64 = 1.37657754143519042600e+02;
137const SA3: f64 = 4.34565877475229228821e+02;
138const SA4: f64 = 6.45387271733267880336e+02;
139const SA5: f64 = 4.29008140027567833386e+02;
140const SA6: f64 = 1.08635005541779435134e+02;
141const SA7: f64 = 6.57024977031928170135e+00;
142const SA8: f64 = -6.04244152148580987438e-02;
143
144const RB0: f64 = -9.86494292470009928597e-03;
145const RB1: f64 = -7.99283237680523006574e-01;
146const RB2: f64 = -1.77579549177547519889e+01;
147const RB3: f64 = -1.60636384855821916062e+02;
148const RB4: f64 = -6.37566443368389627722e+02;
149const RB5: f64 = -1.02509513161107724954e+03;
150const RB6: f64 = -4.83519191608651397019e+02;
151const SB1: f64 = 3.03380607434824582924e+01;
152const SB2: f64 = 3.25792512996573918826e+02;
153const SB3: f64 = 1.53672958608443695994e+03;
154const SB4: f64 = 3.19985821950859553908e+03;
155const SB5: f64 = 2.55305040643316442583e+03;
156const SB6: f64 = 4.74528541206955367215e+02;
157const SB7: f64 = -2.24409524465858183362e+01;
158
159pub fn erf(x: f64) -> f64 {
161 let ix = get_high_word(x) & 0x7fffffff;
162 let sign = if x.is_sign_negative() { -1.0 } else { 1.0 };
163 if ix >= 0x7ff00000 {
164 return if ix == 0x7ff00000 { sign } else { f64::NAN };
166 }
167
168 if ix < 0x3feb0000 {
169 if ix < 0x3e300000 {
171 return 0.125 * (8.0 * x + EFX8 * x);
173 }
174 let z = x * x;
175 let r = PP0 + z * (PP1 + z * (PP2 + z * (PP3 + z * PP4)));
176 let s = 1.0 + z * (QQ1 + z * (QQ2 + z * (QQ3 + z * (QQ4 + z * QQ5))));
177 let y = r / s;
178 return x + x * y;
179 }
180 if ix < 0x40180000 {
181 return sign * (1.0 - erfc_raw(x.abs(), ix));
183 }
184 return sign * (1.0 - 1.0e-300);
186}
187
188pub fn erfc(x: f64) -> f64 {
190 let ix = get_high_word(x) & 0x7fffffff;
191 let sign = if x.is_sign_negative() { -1.0 } else { 1.0 };
192 if ix >= 0x7ff00000 {
193 return if sign > 0.0 { 0.0 } else { 2.0 };
195 }
196 if ix < 0x3feb0000 {
197 if ix < 0x3c700000 {
199 return 1.0 - x;
201 }
202 let z = x * x;
203 let r = PP0 + z * (PP1 + z * (PP2 + z * (PP3 + z * PP4)));
204 let s = 1.0 + z * (QQ1 + z * (QQ2 + z * (QQ3 + z * (QQ4 + z * QQ5))));
205 let y = r / s;
206 if sign < 0.0 || ix < 0x3fd00000 {
207 return 1.0 - (x + x * y);
209 }
210 return 0.5 - (x - 0.5 + x * y);
211 }
212 if ix < 0x403c0000 {
213 if sign < 0.0 {
215 return 2.0 - erfc_raw(fabs(x), ix);
216 } else {
217 return erfc_raw(fabs(x), ix);
218 }
219 }
220 if sign < 0.0 { 2.0 } else { 0.0 }
222}
223
224fn erfc_raw(x: f64, ix: u32) -> f64 {
226 let r;
227 let big_s;
228 let z;
229 if ix < 0x3ff40000 {
230 let s = x - 1.0;
232 let p = PA0 + s * (PA1 + s * (PA2 + s * (PA3 + s * (PA4 + s * (PA5 + s * PA6)))));
233 let q = 1.0 + s * (QA1 + s * (QA2 + s * (QA3 + s * (QA4 + s * (QA5 + s * QA6)))));
234 return 1.0 - ERX - p / q;
235 }
236 let s = 1.0 / (x * x);
237 if ix < 0x4006db6d {
238 r = RA0 + s * (RA1 + s * (RA2 + s * (RA3 + s * (RA4 + s * (RA5 + s * (RA6 + s * RA7))))));
240 big_s = 1.0
241 + s * (SA1
242 + s * (SA2 + s * (SA3 + s * (SA4 + s * (SA5 + s * (SA6 + s * (SA7 + s * SA8)))))));
243 } else {
244 r = RB0 + s * (RB1 + s * (RB2 + s * (RB3 + s * (RB4 + s * (RB5 + s * RB6)))));
245 big_s =
246 1.0 + s * (SB1 + s * (SB2 + s * (SB3 + s * (SB4 + s * (SB5 + s * (SB6 + s * SB7))))));
247 }
248 z = with_set_low_word(x, 0);
249 (-z * z - 0.5625).exp() * ((z - x) * (z + x) + r / big_s).exp() / x
250}
251
252#[inline]
254fn get_high_word(x: f64) -> u32 {
255 (x.to_bits() >> 32) as u32
256}
257
258#[inline]
259fn with_set_low_word(f: f64, lo: u32) -> f64 {
260 let mut tmp = f.to_bits();
261 tmp &= 0xffffffff_00000000;
262 tmp |= lo as u64;
263 f64::from_bits(tmp)
264}
265
266#[inline]
267fn fabs(x: f64) -> f64 {
268 f64::from_bits(x.to_bits() & 0x7fffffffffffffff)
269}
270
271use std::simd::{
288 LaneCount, Simd, StdFloat, SupportedLaneCount, cmp::SimdPartialOrd, num::SimdUint,
289 prelude::SimdFloat,
290};
291
292use crate::kernels::scientific::distributions::shared::constants::SQRT_PI;
293
294const PP: [f64; 5] = [
298 1.28379167095512558561e-01,
299 -3.25042107247001499370e-01,
300 -2.84817495755985104766e-02,
301 -5.77027029648944159157e-03,
302 -2.37630166566501626084e-05,
303];
304const QQ: [f64; 5] = [
305 3.97917223959155352819e-01,
306 6.50222499887672944485e-02,
307 5.08130628187576562776e-03,
308 1.32494738004321644526e-04,
309 -3.96022827877536812320e-06,
310];
311const PA: [f64; 7] = [
313 -2.36211856075265944077e-03,
314 4.14856118683748331666e-01,
315 -3.72207876035701323847e-01,
316 3.18346619901161753674e-01,
317 -1.10894694282396677476e-01,
318 3.54783043256182359371e-02,
319 -2.16637559486879084300e-03,
320];
321const QA: [f64; 7] = [
322 0.0,
323 1.06420880400844228286e-01,
324 5.40397917702171048937e-01,
325 7.18286544141962662868e-02,
326 1.26171219808761642112e-01,
327 1.36370839120290507362e-02,
328 1.19844998467991074170e-02,
329];
330const RA: [f64; 8] = [
332 -9.86494403484714822705e-03,
333 -6.93858572707181764372e-01,
334 -1.05586262253232909814e+01,
335 -6.23753324503260060396e+01,
336 -1.62396669462573470355e+02,
337 -1.84605092906711035994e+02,
338 -8.12874355063065934246e+01,
339 -9.81432934416914548592e+00,
340];
341const SA: [f64; 9] = [
342 0.0,
343 1.96512716674392571292e+01,
344 1.37657754143519042600e+02,
345 4.34565877475229228821e+02,
346 6.45387271733267880336e+02,
347 4.29008140027567833386e+02,
348 1.08635005541779435134e+02,
349 6.57024977031928170135e+00,
350 -6.04244152148580987438e-02,
351];
352const RB: [f64; 7] = [
354 -9.86494292470009928597e-03,
355 -7.99283237680523006574e-01,
356 -1.77579549177547519889e+01,
357 -1.60636384855821916062e+02,
358 -6.37566443368389627722e+02,
359 -1.02509513161107724954e+03,
360 -4.83519191608651397019e+02,
361];
362const SB: [f64; 8] = [
363 0.0,
364 3.03380607434824582924e+01,
365 3.25792512996573918826e+02,
366 1.53672958608443695994e+03,
367 3.19985821950859553908e+03,
368 2.55305040643316442583e+03,
369 4.74528541206955367215e+02,
370 -2.24409524465858183362e+01,
371];
372
373#[inline(always)]
374fn hi_u32<const LANES: usize>(x: Simd<f64, LANES>) -> Simd<u32, LANES>
375where
376 LaneCount<LANES>: SupportedLaneCount,
377{
378 (x.to_bits() >> Simd::splat(32)).cast()
380}
381
382#[inline(always)]
383fn clear_lo32<const LANES: usize>(x: Simd<f64, LANES>) -> Simd<f64, LANES>
384where
385 LaneCount<LANES>: SupportedLaneCount,
386{
387 let bits: Simd<u64, LANES> = x.to_bits();
388 let mask = Simd::splat(0xffff_ffff_0000_0000_u64);
389 Simd::<f64, LANES>::from_bits(bits & mask)
390}
391
392#[inline(always)]
394pub fn erf_simd<const LANES: usize>(x: Simd<f64, LANES>) -> Simd<f64, LANES>
395where
396 LaneCount<LANES>: SupportedLaneCount,
397{
398 let ax = x.abs();
400 let sign = x
401 .is_sign_negative()
402 .select(Simd::splat(-1.0), Simd::splat(1.0));
403
404 let hx = hi_u32(ax); let region1 = hx.simd_lt(Simd::splat(0x3feb0000)); let region2 = hx.simd_ge(Simd::splat(0x3feb0000)) & hx.simd_lt(Simd::splat(0x3ff40000)); let region3 = hx.simd_ge(Simd::splat(0x3ff40000)) & hx.simd_lt(Simd::splat(0x40180000)); let region5 = hx.simd_ge(Simd::splat(0x40180000)); let mut y = Simd::splat(0.0);
412
413 if region1.any() {
415 let z = ax * ax;
416 let p = Simd::splat(PP[4])
417 .mul_add(z, Simd::splat(PP[3]))
418 .mul_add(z, Simd::splat(PP[2]))
419 .mul_add(z, Simd::splat(PP[1]))
420 .mul_add(z, Simd::splat(PP[0]));
421 let q = Simd::splat(QQ[4])
422 .mul_add(z, Simd::splat(QQ[3]))
423 .mul_add(z, Simd::splat(QQ[2]))
424 .mul_add(z, Simd::splat(QQ[1]))
425 .mul_add(z, Simd::splat(QQ[0]))
426 .mul_add(z, Simd::splat(1.0));
427 let r1 = x + x * (p / q);
428 y = region1.cast().select(r1, y);
429 }
430
431 if region2.any() {
433 let s = ax - Simd::splat(1.0);
434 let p = Simd::splat(PA[6])
435 .mul_add(s, Simd::splat(PA[5]))
436 .mul_add(s, Simd::splat(PA[4]))
437 .mul_add(s, Simd::splat(PA[3]))
438 .mul_add(s, Simd::splat(PA[2]))
439 .mul_add(s, Simd::splat(PA[1]))
440 .mul_add(s, Simd::splat(PA[0]));
441 let q = Simd::splat(QA[6])
442 .mul_add(s, Simd::splat(QA[5]))
443 .mul_add(s, Simd::splat(QA[4]))
444 .mul_add(s, Simd::splat(QA[3]))
445 .mul_add(s, Simd::splat(QA[2]))
446 .mul_add(s, Simd::splat(QA[1]))
447 .mul_add(s, Simd::splat(1.0));
448 let r2 = sign * (Simd::splat(ERX) + p / q);
449 y = region2.cast().select(r2, y);
450 }
451
452 if region3.any() | region5.any() {
454 let z = clear_lo32(ax); let inv_x2 = Simd::splat(1.0) / (ax * ax);
456
457 let m3 = region3.cast() & ax.simd_lt(Simd::splat(2.857143));
459 if m3.any() {
460 let rpoly = Simd::splat(RA[7])
461 .mul_add(inv_x2, Simd::splat(RA[6]))
462 .mul_add(inv_x2, Simd::splat(RA[5]))
463 .mul_add(inv_x2, Simd::splat(RA[4]))
464 .mul_add(inv_x2, Simd::splat(RA[3]))
465 .mul_add(inv_x2, Simd::splat(RA[2]))
466 .mul_add(inv_x2, Simd::splat(RA[1]))
467 .mul_add(inv_x2, Simd::splat(RA[0]));
468
469 let spol = Simd::splat(SA[8])
470 .mul_add(inv_x2, Simd::splat(SA[7]))
471 .mul_add(inv_x2, Simd::splat(SA[6]))
472 .mul_add(inv_x2, Simd::splat(SA[5]))
473 .mul_add(inv_x2, Simd::splat(SA[4]))
474 .mul_add(inv_x2, Simd::splat(SA[3]))
475 .mul_add(inv_x2, Simd::splat(SA[2]))
476 .mul_add(inv_x2, Simd::splat(SA[1]))
477 .mul_add(inv_x2, Simd::splat(1.0));
478
479 let exp_term = (-z * z - Simd::splat(0.5625)).exp()
480 * ((z - ax) * (z + ax) + rpoly / spol).exp()
481 / ax;
482 let r3 = sign * (Simd::splat(1.0) - exp_term);
483 y = m3.select(r3, y);
484 }
485
486 let m4 = (region3.cast() & ax.simd_ge(Simd::splat(2.857143))) | region5.cast();
488 if m4.any() {
489 let rpoly = Simd::splat(RB[6])
490 .mul_add(inv_x2, Simd::splat(RB[5]))
491 .mul_add(inv_x2, Simd::splat(RB[4]))
492 .mul_add(inv_x2, Simd::splat(RB[3]))
493 .mul_add(inv_x2, Simd::splat(RB[2]))
494 .mul_add(inv_x2, Simd::splat(RB[1]))
495 .mul_add(inv_x2, Simd::splat(RB[0]));
496
497 let spol = Simd::splat(SB[7])
498 .mul_add(inv_x2, Simd::splat(SB[6]))
499 .mul_add(inv_x2, Simd::splat(SB[5]))
500 .mul_add(inv_x2, Simd::splat(SB[4]))
501 .mul_add(inv_x2, Simd::splat(SB[3]))
502 .mul_add(inv_x2, Simd::splat(SB[2]))
503 .mul_add(inv_x2, Simd::splat(SB[1]))
504 .mul_add(inv_x2, Simd::splat(1.0));
505
506 let exp_term = (-z * z - Simd::splat(0.5625)).exp()
507 * ((z - ax) * (z + ax) + rpoly / spol).exp()
508 / ax;
509 let r4 = sign * (Simd::splat(1.0) - exp_term);
510 y = m4.select(r4, y);
511 }
512 }
513
514 let infmask = x.is_infinite();
516 y = infmask.select(sign, y);
517 y = x.is_nan().select(x, y);
518 y
519}
520
521#[inline(always)]
523pub fn erfc_simd<const LANES: usize>(x: Simd<f64, LANES>) -> Simd<f64, LANES>
524where
525 LaneCount<LANES>: SupportedLaneCount,
526{
527 let ax = x.abs();
528 let signneg = x.is_sign_negative();
529 let hx = hi_u32(ax);
530
531 let r1_mask = hx.simd_lt(Simd::splat(0x3feb0000)); let r2_mask = hx.simd_ge(Simd::splat(0x3feb0000)) & hx.simd_lt(Simd::splat(0x3ff40000)); let r3_mask = hx.simd_ge(Simd::splat(0x3ff40000)) & hx.simd_lt(Simd::splat(0x40180000)); let r5_mask = hx.simd_ge(Simd::splat(0x40180000)); let mut out = Simd::splat(0.0);
537
538 if r1_mask.any() {
540 let z = ax * ax;
541 let p = Simd::splat(PP[4])
542 .mul_add(z, Simd::splat(PP[3]))
543 .mul_add(z, Simd::splat(PP[2]))
544 .mul_add(z, Simd::splat(PP[1]))
545 .mul_add(z, Simd::splat(PP[0]));
546 let q = Simd::splat(QQ[4])
547 .mul_add(z, Simd::splat(QQ[3]))
548 .mul_add(z, Simd::splat(QQ[2]))
549 .mul_add(z, Simd::splat(QQ[1]))
550 .mul_add(z, Simd::splat(QQ[0]))
551 .mul_add(z, Simd::splat(1.0));
552 let y = p / q;
553
554 let sub_a = signneg.cast() | hx.simd_lt(Simd::splat(0x3fd00000));
556 let r_a = Simd::splat(1.0) - (x + x * y);
557
558 let r_b = Simd::splat(0.5) - (x - Simd::splat(0.5) + x * y);
560
561 let r1 = sub_a.cast().select(r_a, r_b);
562 out = r1_mask.cast().select(r1, out);
563 }
564
565 if r2_mask.any() {
567 let s = ax - Simd::splat(1.0);
568 let p = Simd::splat(PA[6])
569 .mul_add(s, Simd::splat(PA[5]))
570 .mul_add(s, Simd::splat(PA[4]))
571 .mul_add(s, Simd::splat(PA[3]))
572 .mul_add(s, Simd::splat(PA[2]))
573 .mul_add(s, Simd::splat(PA[1]))
574 .mul_add(s, Simd::splat(PA[0]));
575 let q = Simd::splat(QA[6])
576 .mul_add(s, Simd::splat(QA[5]))
577 .mul_add(s, Simd::splat(QA[4]))
578 .mul_add(s, Simd::splat(QA[3]))
579 .mul_add(s, Simd::splat(QA[2]))
580 .mul_add(s, Simd::splat(QA[1]))
581 .mul_add(s, Simd::splat(1.0));
582 let t = p / q;
583
584 let r_pos = Simd::splat(1.0 - ERX) - t; let r_neg = Simd::splat(1.0) + (Simd::splat(ERX) + t); let r2 = signneg.select(r_neg, r_pos);
588 out = r2_mask.cast().select(r2, out);
589 }
590
591 if (r3_mask | r5_mask).any() {
593 let z = clear_lo32(ax);
594 let inv_x2 = Simd::splat(1.0) / (ax * ax);
595
596 let m3 = r3_mask.cast() & ax.simd_lt(Simd::splat(2.857143));
598 if m3.any() {
599 let rpoly = Simd::splat(RA[7])
600 .mul_add(inv_x2, Simd::splat(RA[6]))
601 .mul_add(inv_x2, Simd::splat(RA[5]))
602 .mul_add(inv_x2, Simd::splat(RA[4]))
603 .mul_add(inv_x2, Simd::splat(RA[3]))
604 .mul_add(inv_x2, Simd::splat(RA[2]))
605 .mul_add(inv_x2, Simd::splat(RA[1]))
606 .mul_add(inv_x2, Simd::splat(RA[0]));
607
608 let spol = Simd::splat(SA[8])
609 .mul_add(inv_x2, Simd::splat(SA[7]))
610 .mul_add(inv_x2, Simd::splat(SA[6]))
611 .mul_add(inv_x2, Simd::splat(SA[5]))
612 .mul_add(inv_x2, Simd::splat(SA[4]))
613 .mul_add(inv_x2, Simd::splat(SA[3]))
614 .mul_add(inv_x2, Simd::splat(SA[2]))
615 .mul_add(inv_x2, Simd::splat(SA[1]))
616 .mul_add(inv_x2, Simd::splat(1.0));
617
618 let exp_term = (-z * z - Simd::splat(0.5625)).exp()
619 * ((z - ax) * (z + ax) + rpoly / spol).exp()
620 / ax;
621
622 let r3 = signneg.select(Simd::splat(2.0) - exp_term, exp_term);
623 out = m3.select(r3, out);
624 }
625
626 let m4 = (r3_mask.cast() & ax.simd_ge(Simd::splat(2.857143))) | r5_mask.cast();
628 if m4.any() {
629 let rpoly = Simd::splat(RB[6])
630 .mul_add(inv_x2, Simd::splat(RB[5]))
631 .mul_add(inv_x2, Simd::splat(RB[4]))
632 .mul_add(inv_x2, Simd::splat(RB[3]))
633 .mul_add(inv_x2, Simd::splat(RB[2]))
634 .mul_add(inv_x2, Simd::splat(RB[1]))
635 .mul_add(inv_x2, Simd::splat(RB[0]));
636
637 let spol = Simd::splat(SB[7])
638 .mul_add(inv_x2, Simd::splat(SB[6]))
639 .mul_add(inv_x2, Simd::splat(SB[5]))
640 .mul_add(inv_x2, Simd::splat(SB[4]))
641 .mul_add(inv_x2, Simd::splat(SB[3]))
642 .mul_add(inv_x2, Simd::splat(SB[2]))
643 .mul_add(inv_x2, Simd::splat(SB[1]))
644 .mul_add(inv_x2, Simd::splat(1.0));
645
646 let exp_term = (-z * z - Simd::splat(0.5625)).exp()
647 * ((z - ax) * (z + ax) + rpoly / spol).exp()
648 / ax;
649
650 let r4_5 = signneg.select(Simd::splat(2.0) - exp_term, exp_term);
653 out = m4.select(r4_5, out);
654 }
655 }
656
657 out = x
659 .is_infinite()
660 .select(signneg.select(Simd::splat(2.0), Simd::splat(0.0)), out);
661 out = x.is_nan().select(x, out);
662 out
663}
664
665#[inline(always)]
671pub fn erfc_inv(p: f64) -> f64 {
672 if p.is_nan() {
674 return f64::NAN;
675 }
676 if p <= 0.0 {
677 return f64::INFINITY;
678 } if p >= 2.0 {
680 return -f64::INFINITY;
681 } if p == 1.0 {
683 return 0.0;
684 } let (pp, sign) = if p < 1.0 { (p, 1.0) } else { (2.0 - p, -1.0) };
688
689 let t = (-2.0 * (pp * 0.5).ln()).sqrt(); let mut x = t - ((0.70711) / t + 0.000542 / (t * t));
693
694 for _ in 0..2 {
698 let err = erfc(x) - pp;
699 let der = -2.0 / SQRT_PI * (-x * x).exp();
700 x -= err / der;
701 }
702
703 sign * x }
705
706#[cfg(test)]
707mod tests {
708 use core::simd::Simd;
709
710 use super::*;
711
712 #[inline]
714 fn erf1(x: f64) -> f64 {
715 erf_simd::<1>(Simd::from_array([x])).to_array()[0]
716 }
717 #[inline]
718 fn erfc1(x: f64) -> f64 {
719 erfc_simd::<1>(Simd::from_array([x])).to_array()[0]
720 }
721
722 #[test] fn erf_zero() {
728 assert!((erf1(0.0) - 0.0).abs() < 1e-16);
729 }
730
731 #[test] fn erf_half() {
733 assert!((erf1(0.5) - 0.5204998778130465).abs() < 1e-15);
734 }
735
736 #[test] fn erfc_half() {
738 assert!((erfc1(0.5) - 0.4795001221869535).abs() < 1e-15);
739 }
740
741 #[test] fn erf_one() {
747 assert!((erf1(1.0) - 0.8427007929497148).abs() < 1e-15);
748 }
749
750 #[test] fn erf_minus_one() {
752 assert!((erf1(-1.0) + 0.8427007929497148).abs() < 1e-15);
753 }
754
755 #[test] fn erfc_one() {
757 assert!((erfc1(1.0) - 0.15729920705028516).abs() < 1e-15);
758 }
759
760 #[test] fn erfc_minus_one() {
762 assert!((erfc1(-1.0) - 1.8427007929497148).abs() < 1e-15);
763 }
764
765 #[test] fn erf_two() {
771 assert!((erf1(2.0) - 0.9953222650189527).abs() < 1e-15);
772 }
773
774 #[test] fn erfc_two() {
776 assert!((erfc1(2.0) - 0.004677734981047266).abs() < 1e-15);
777 }
778
779 #[test] fn erf_three() {
781 assert!((erf1(3.0) - 0.9999779095030014).abs() < 1e-15);
782 }
783
784 #[test] fn erfc_three() {
786 assert!((erfc1(3.0) - 2.2090496998585445e-05).abs() < 1e-15);
787 }
788
789 #[test] fn erf_four() {
795 assert!((erf1(4.0) - 0.9999999845827421).abs() < 1e-15);
796 }
797
798 #[test] fn erfc_four() {
800 assert!((erfc1(4.0) - 1.541725790028002e-08).abs() < 1e-18);
801 }
802
803 #[test] fn erf_six() {
809 assert_eq!(erf1(6.0), 1.0);
810 }
811
812 #[test] fn erfc_six() {
814 assert!((erfc1(6.0) - 2.1519736712498913e-17).abs() < 1e-18);
815 }
816
817 #[test] fn erf_odd_symmetry() {
823 let x = 1.2345;
824 assert!((erf1(-x) + erf1(x)) == 0.0);
825 }
826
827 #[test] fn erfc_even_complement() {
829 let x = 0.987;
830 assert!((erfc1(-x) - (2.0 - erfc1(x))) == 0.0);
831 }
832
833 #[test] fn erf_erfc_sum_identity() {
835 let xs = [-1.7, 1.7];
836 for &x in &xs {
837 assert!((erf1(x) + erfc1(x) - 1.0).abs() == 0.0);
838 }
839 }
840
841 #[test] fn erf_pos_inf() {
847 assert_eq!(erf1(f64::INFINITY), 1.0);
848 }
849
850 #[test] fn erf_neg_inf() {
852 assert_eq!(erf1(f64::NEG_INFINITY), -1.0);
853 }
854
855 #[test] fn erfc_pos_inf() {
857 assert_eq!(erfc1(f64::INFINITY), 0.0);
858 }
859
860 #[test] fn erfc_neg_inf() {
862 assert_eq!(erfc1(f64::NEG_INFINITY), 2.0);
863 }
864
865 #[test] fn erf_nan() {
867 assert!(erf1(f64::NAN).is_nan());
868 }
869
870 #[test] fn erfc_nan() {
872 assert!(erfc1(f64::NAN).is_nan());
873 }
874
875 #[inline]
879 fn erf_simd4(x: [f64; 4]) -> [f64; 4] {
880 erf_simd::<4>(Simd::from_array(x)).to_array()
881 }
882 #[inline]
883 fn erfc_simd4(x: [f64; 4]) -> [f64; 4] {
884 erfc_simd::<4>(Simd::from_array(x)).to_array()
885 }
886
887 #[test] fn simd_region1() {
893 let input = [0.0, 0.5, -0.5, 0.1];
894 let expect = [
895 0.0,
896 0.5204998778130465,
897 -0.5204998778130465,
898 0.1124629160182849,
899 ];
900 let actual = erf_simd4(input);
901 for (a, e) in actual.iter().zip(expect) {
902 assert!((a - e).abs() < 1e-15);
903 }
904 }
905
906 #[test] fn simd_erfc_region1() {
908 let input = [0.0, 0.5, -0.5, 0.1];
909 let expect = [
910 1.0,
911 0.4795001221869535,
912 1.5204998778130465,
913 0.8875370839817152,
914 ];
915 let actual = erfc_simd4(input);
916 for (a, e) in actual.iter().zip(expect) {
917 assert!((a - e).abs() < 1e-15);
918 }
919 }
920
921 #[test] fn simd_region2() {
927 let input = [1.0, -1.0, 1.2, -1.2];
928 let expect = [
929 0.8427007929497148,
930 -0.8427007929497148,
931 0.9103139782296353,
932 -0.9103139782296353,
933 ];
934 let actual = erf_simd4(input);
935 for (a, e) in actual.iter().zip(expect) {
936 assert!((a - e).abs() < 1e-15);
937 }
938 }
939
940 #[test] fn simd_erfc_region2() {
942 let input = [1.0, -1.0, 1.2, -1.2];
943 let expect = [
944 0.15729920705028516,
945 1.8427007929497148,
946 0.08968602177036462,
947 1.9103139782296354,
948 ];
949 let actual = erfc_simd4(input);
950 for (a, e) in actual.iter().zip(expect) {
951 assert!((a - e).abs() < 1e-15);
952 }
953 }
954
955 #[test] fn simd_region3() {
961 let input = [2.0, -2.0, 2.5, -2.5];
962 let expect = [
963 0.9953222650189527,
964 -0.9953222650189527,
965 0.999593047982555,
966 -0.999593047982555,
967 ];
968 let actual = erf_simd4(input);
969 for (a, e) in actual.iter().zip(expect) {
970 assert!((a - e).abs() < 1e-15);
971 }
972 }
973
974 #[test] fn simd_erfc_region3() {
976 let input = [2.0, -2.0, 2.5, -2.5];
977 let expect = [
978 4.6777349810472662e-03,
979 1.9953222650189528e+00,
980 4.0695201744495886e-04,
981 1.9995930479825550e+00,
982 ];
983 let actual = erfc_simd4(input);
984 for (a, e) in actual.iter().zip(expect) {
985 assert!((a - e).abs() < 1e-15);
986 }
987 }
988
989 #[test] fn simd_region4_5() {
995 let input = [3.0, -3.0, 6.0, -6.0];
996 let expect = [0.9999779095030014, -0.9999779095030014, 1., -1.];
997 let actual = erf_simd4(input);
998 for (a, e) in actual.iter().zip(expect) {
999 assert!((a - e).abs() < 1e-14);
1000 }
1001 }
1002
1003 #[test] fn simd_erfc_region4_5() {
1005 let input = [3.0, -3.0, 6.0, -6.0];
1006 let expect = [
1007 2.2090496998585445e-05,
1008 1.9999779095030015e+00,
1009 2.1519736712498913e-17,
1010 2.0000000000000000e+00,
1011 ];
1012 let actual = erfc_simd4(input);
1013 for (a, e) in actual.iter().zip(expect) {
1014 assert!((a - e).abs() < 1e-15);
1015 }
1016 }
1017
1018 #[test] fn simd_specials() {
1024 let input = [-0.0, f64::INFINITY, f64::NEG_INFINITY, f64::NAN];
1025 let expect = [-0.0, 1.0, -1.0, f64::NAN];
1026 let actual = erf_simd4(input);
1027 for (a, e) in actual.iter().zip(expect) {
1028 if e.is_nan() {
1029 assert!(a.is_nan());
1030 } else {
1031 assert_eq!(*a, e);
1032 }
1033 }
1034 }
1035
1036 #[test] fn simd_erfc_specials() {
1038 let input = [-0.0, f64::INFINITY, f64::NEG_INFINITY, f64::NAN];
1039 let expect = [1.0, 0.0, 2.0, f64::NAN];
1040 let actual = erfc_simd4(input);
1041 for (a, e) in actual.iter().zip(expect) {
1042 if e.is_nan() {
1043 assert!(a.is_nan());
1044 } else {
1045 assert_eq!(*a, e);
1046 }
1047 }
1048 }
1049
1050 #[test]
1054 fn simd_symmetry_complement() {
1055 let x = [1.2345, -1.2345, 0.987, -0.987];
1056 let erf = erf_simd4(x);
1057 let erfc = erfc_simd4(x);
1058 assert!((erf[0] + erf[1]).abs() == 0.0);
1060 assert!((erf[2] + erf[3]).abs() == 0.0);
1061 assert!((erfc[0] - (2.0 - erfc[1])).abs() < 1e-16);
1063 assert!((erfc[2] - (2.0 - erfc[3])).abs() < 1e-16);
1064 for i in 0..4 {
1066 assert!((erf[i] + erfc[i] - 1.0).abs() == 0.0);
1067 }
1068 }
1069}
1070
1071#[test] fn erf_simd_lanes() {
1077 let v = Simd::<f64, 4>::from_array([0.0, 1.0, 2.0, 3.0]);
1078 let y = erf_simd::<4>(v).to_array();
1079 let expect = [
1080 0.,
1081 0.8427007929497148,
1082 0.9953222650189527,
1083 0.9999779095030014,
1084 ];
1085 for (yi, ei) in y.iter().zip(expect.iter()) {
1086 assert!((yi - ei).abs() < 1e-15);
1087 }
1088}
1089
1090#[test] fn erfc_simd_lanes() {
1092 let v = Simd::<f64, 4>::from_array([-1.0, 0.0, 1.0, 2.0]);
1093 let y = erfc_simd::<4>(v).to_array();
1094 let expect = [
1095 1.8427007929497148,
1096 1.,
1097 0.15729920705028516,
1098 0.004677734981047266,
1099 ];
1100 for (yi, ei) in y.iter().zip(expect.iter()) {
1101 assert!((yi - ei).abs() < 1e-15);
1102 }
1103}