/* * // Copyright (c) Radzivon Bartoshyk 9/2025. All rights reserved. * // * // Redistribution and use in source and binary forms, with or without modification, * // are permitted provided that the following conditions are met: * // * // 1. Redistributions of source code must retain the above copyright notice, this * // list of conditions and the following disclaimer. * // * // 2. Redistributions in binary form must reproduce the above copyright notice, * // this list of conditions and the following disclaimer in the documentation * // and/or other materials provided with the distribution. * // * // 3. Neither the name of the copyright holder nor the names of its * // contributors may be used to endorse or promote products derived from * // this software without specific prior written permission. * // * // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ use crate::common::f_fmla; use crate::double_double::DoubleDouble; use crate::logs::{fast_log_d_to_dd, fast_log_dd}; use crate::polyeval::{f_polyeval4, f_polyeval5}; #[cold] fn inverf_0p06_to_0p75(x: DoubleDouble) -> DoubleDouble { // First step rational approximant is generated, but it's ill-conditioned, thus // we're using taylor expansion to create Newton form at the point. // Generated in Wolfram Mathematica: // <75, MaxIterations->100] // num=Numerator[approx][[1]]; // den=Denominator[approx][[1]]; // poly=den; // coeffs=CoefficientList[poly,z]; // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] // x0=SetPrecision[0.5625,75]; // NumberForm[Series[num[x],{x,x0,50}], ExponentFunction->(Null&)] // coeffs=Table[SeriesCoefficient[num[x],{x,x0,k}],{k,0,9}]; // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]; const P: [(u64, u64); 10] = [ (0xbc3e06eda42202a0, 0x3f93c2fc5d00e0c8), (0xbc6eb374406b33b4, 0xbfc76fcfd022e3ff), (0xbc857822d7ffd282, 0x3fe6f8443546010a), (0x3c68269c66dfb28a, 0xbff80996754ceb79), (0x3c543dce8990a9f9, 0x3ffcf778d5ef0504), (0xbc72fc55f73765f6, 0xbff433be821423d0), (0xbc66d05fb37c8592, 0x3fdf15f19e9d8da4), (0x3c56dfb85e83a2c5, 0xbfb770b6827e0829), (0x3bff1472ecdfa403, 0x3f7a98a2980282bb), (0x3baffb33d69d6276, 0xbf142a246fd2c07c), ]; let x2 = DoubleDouble::quick_mult(x, x); let vz = DoubleDouble::full_add_f64(x2, -0.5625); let vx2 = vz * vz; let vx4 = vx2 * vx2; let vx8 = vx4 * vx4; let p0 = DoubleDouble::mul_add( vz, DoubleDouble::from_bit_pair(P[1]), DoubleDouble::from_bit_pair(P[0]), ); let p1 = DoubleDouble::mul_add( vz, DoubleDouble::from_bit_pair(P[3]), DoubleDouble::from_bit_pair(P[2]), ); let p2 = DoubleDouble::mul_add( vz, DoubleDouble::from_bit_pair(P[5]), DoubleDouble::from_bit_pair(P[4]), ); let p3 = DoubleDouble::mul_add( vz, DoubleDouble::from_bit_pair(P[7]), DoubleDouble::from_bit_pair(P[6]), ); let p4 = DoubleDouble::mul_add( vz, DoubleDouble::from_bit_pair(P[9]), DoubleDouble::from_bit_pair(P[8]), ); let q0 = DoubleDouble::mul_add(vx2, p1, p0); let q1 = DoubleDouble::mul_add(vx2, p3, p2); let r0 = DoubleDouble::mul_add(vx4, q1, q0); let num = DoubleDouble::mul_add(vx8, p4, r0); // Generated in Wolfram Mathematica: // <75, MaxIterations->100] // num=Numerator[approx][[1]]; // den=Denominator[approx][[1]]; // coeffs=CoefficientList[poly,z]; // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] // x0=SetPrecision[0.5625,75]; // NumberForm[Series[den[x],{x,x0,50}], ExponentFunction->(Null&)] // coeffs=Table[SeriesCoefficient[den[x],{x,x0,k}],{k,0,9}]; // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]; const Q: [(u64, u64); 10] = [ (0xbc36337f24e57cb9, 0x3f92388d5d757e3a), (0xbc63dfae43d60e0b, 0xbfc6ca7da581358c), (0xbc77656389bd0e62, 0x3fe7c82ce417b4e0), (0xbc93679667bef2f0, 0xbffad58651fd1a51), (0x3ca2c6cb9eb17fb4, 0x4001bdb67e93a242), (0xbc9b58961ba253bc, 0xbffbdaeff6fbb81c), (0x3c7861f549c6aa61, 0x3fe91b12cf47da3a), (0xbc696dfd665b2f5e, 0xbfc7c5d0ffb7f1da), (0x3c1552b0ec0ba7b3, 0x3f939ada247f7609), (0xbbcaa226fb7b30a8, 0xbf41be65038ccfe6), ]; let p0 = DoubleDouble::mul_add( vz, DoubleDouble::from_bit_pair(Q[1]), DoubleDouble::from_bit_pair(Q[0]), ); let p1 = DoubleDouble::mul_add( vz, DoubleDouble::from_bit_pair(Q[3]), DoubleDouble::from_bit_pair(Q[2]), ); let p2 = DoubleDouble::mul_add( vz, DoubleDouble::from_bit_pair(Q[5]), DoubleDouble::from_bit_pair(Q[4]), ); let p3 = DoubleDouble::mul_add( vz, DoubleDouble::from_bit_pair(Q[7]), DoubleDouble::from_bit_pair(Q[6]), ); let p4 = DoubleDouble::mul_add( vz, DoubleDouble::from_bit_pair(Q[9]), DoubleDouble::from_bit_pair(Q[8]), ); let q0 = DoubleDouble::mul_add(vx2, p1, p0); let q1 = DoubleDouble::mul_add(vx2, p3, p2); let r0 = DoubleDouble::mul_add(vx4, q1, q0); let den = DoubleDouble::mul_add(vx8, p4, r0); let r = DoubleDouble::div(num, den); DoubleDouble::quick_mult(r, x) } #[inline] fn inverf_asympt_small(z: DoubleDouble, zeta_sqrt: DoubleDouble) -> DoubleDouble { // Generated in Wolfram Mathematica: // <90] // num=Numerator[approx]; // den=Denominator[approx]; // poly=num; // coeffs=CoefficientList[poly,z]; // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] const P: [(u64, u64); 11] = [ (0x3c936555853a8b2c, 0x3ff0001df06a2515), (0x3cea488e802db3c3, 0x404406ba373221da), (0xbce27d42419754e3, 0x407b0442e38a9597), (0xbd224a407624cbdf, 0x409c9277e31ef446), (0x3d4f16ce65d6fea0, 0x40aec3ec005b1d8a), (0x3d105bc37bc61b58, 0x40b46be8f860f4d9), (0x3d5ca133dcdecaa0, 0x40b3826e6a32dad7), (0x3d1d52013ba8aa38, 0x40aae93a603cf3ea), (0xbd07a75306df0fc3, 0x4098ab8357dc2e51), (0x3d1bb6770bb7a27e, 0x407ebead00879010), (0xbbfcbff4a9737936, 0x3f8936117ccbff83), ]; let z2 = DoubleDouble::quick_mult(z, z); let z4 = DoubleDouble::quick_mult(z2, z2); let z8 = DoubleDouble::quick_mult(z4, z4); let q0 = DoubleDouble::mul_add( DoubleDouble::from_bit_pair(P[1]), z, DoubleDouble::from_bit_pair(P[0]), ); let q1 = DoubleDouble::mul_add( DoubleDouble::from_bit_pair(P[3]), z, DoubleDouble::from_bit_pair(P[2]), ); let q2 = DoubleDouble::mul_add( DoubleDouble::from_bit_pair(P[5]), z, DoubleDouble::from_bit_pair(P[4]), ); let q3 = DoubleDouble::mul_add( DoubleDouble::from_bit_pair(P[7]), z, DoubleDouble::from_bit_pair(P[6]), ); let q4 = DoubleDouble::mul_add( DoubleDouble::from_bit_pair(P[9]), z, DoubleDouble::from_bit_pair(P[8]), ); let r0 = DoubleDouble::mul_add(z2, q1, q0); let r1 = DoubleDouble::mul_add(z2, q3, q2); let s0 = DoubleDouble::mul_add(z4, r1, r0); let s1 = DoubleDouble::mul_add(z2, DoubleDouble::from_bit_pair(P[10]), q4); let num = DoubleDouble::mul_add(z8, s1, s0); // See numerator generation above: // poly=den; // coeffs=CoefficientList[poly,z]; // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] const Q: [(u64, u64); 11] = [ (0x0000000000000000, 0x3ff0000000000000), (0xbc75b1109d4a3262, 0x40440782efaab17f), (0x3d1f7775b207d84f, 0x407b2da74b0d39f2), (0xbd3291fdbab49501, 0x409dac8d9e7c90b2), (0xbd58d8fdd27707a9, 0x40b178dfeffa3192), (0xbd57fc74ad705ce0, 0x40bad19b686f219f), (0x3d4075510031f2cd, 0x40be70a598208cea), (0xbd5442e109152efb, 0x40b9683ef36ae330), (0x3d5398192933962e, 0x40b04b7c4c3ca8ee), (0x3d2d04d03598e303, 0x409bd0080799fbf1), (0x3d2a988eb552ef44, 0x40815a46f12bafe3), ]; let q0 = DoubleDouble::mul_add_f64( DoubleDouble::from_bit_pair(Q[1]), z, f64::from_bits(0x3ff0000000000000), ); let q1 = DoubleDouble::mul_add( DoubleDouble::from_bit_pair(Q[3]), z, DoubleDouble::from_bit_pair(Q[2]), ); let q2 = DoubleDouble::mul_add( DoubleDouble::from_bit_pair(Q[5]), z, DoubleDouble::from_bit_pair(Q[4]), ); let q3 = DoubleDouble::mul_add( DoubleDouble::from_bit_pair(Q[7]), z, DoubleDouble::from_bit_pair(Q[6]), ); let q4 = DoubleDouble::mul_add( DoubleDouble::from_bit_pair(Q[9]), z, DoubleDouble::from_bit_pair(Q[8]), ); let r0 = DoubleDouble::mul_add(z2, q1, q0); let r1 = DoubleDouble::mul_add(z2, q3, q2); let s0 = DoubleDouble::mul_add(z4, r1, r0); let s1 = DoubleDouble::mul_add(z2, DoubleDouble::from_bit_pair(Q[10]), q4); let den = DoubleDouble::mul_add(z8, s1, s0); let r = DoubleDouble::div(num, den); DoubleDouble::quick_mult(r, zeta_sqrt) } // branch for |x| > 0.9999 for extreme tail #[cold] fn inverf_asympt_long(z: DoubleDouble, zeta_sqrt: DoubleDouble) -> DoubleDouble { // First step rational approximant is generated, but it's ill-conditioned, thus // we're using taylor expansion to create Newton form at the point. // Generated in Wolfram Mathematica: // <90] // num=Numerator[approx][[1]]; // den=Denominator[approx][[1]]; // poly=num; // coeffs=CoefficientList[poly,z]; // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] const P: [(u64, u64); 14] = [ (0x3c97612f9b24a614, 0x3ff0000ba84cc7a5), (0xbcee8fe2da463412, 0x40515246546f5d88), (0x3d2fa4a2b891b526, 0x40956b6837159b11), (0x3d5d673ffad4f817, 0x40c5a1aa3be58652), (0x3d8867a1e5506f88, 0x40e65ebb1e1e7c75), (0xbd9bbc0764ed8f5b, 0x40fd2064a652e5c2), (0xbda78e569c0d237f, 0x410a385c627c461c), (0xbdab3123ebc465d7, 0x4110f05ca2b65fe5), (0x3d960def35955192, 0x4110bb079af2fe08), (0xbd97904816054836, 0x410911c24610c11c), (0xbd937745e9192593, 0x40fc603244adca35), (0xbd65fbc476d63050, 0x40e6399103188c21), (0xbd61016ef381cce6, 0x40c6482b44995b89), (0x3c326105c49e5a1a, 0xbfab44bd8b4e3138), ]; let z2 = z * z; let z4 = z2 * z2; let z8 = z4 * z4; let g0 = DoubleDouble::mul_add( z, DoubleDouble::from_bit_pair(P[1]), DoubleDouble::from_bit_pair(P[0]), ); let g1 = DoubleDouble::mul_add( z, DoubleDouble::from_bit_pair(P[3]), DoubleDouble::from_bit_pair(P[2]), ); let g2 = DoubleDouble::mul_add( z, DoubleDouble::from_bit_pair(P[5]), DoubleDouble::from_bit_pair(P[4]), ); let g3 = DoubleDouble::mul_add( z, DoubleDouble::from_bit_pair(P[7]), DoubleDouble::from_bit_pair(P[6]), ); let g4 = DoubleDouble::mul_add( z, DoubleDouble::from_bit_pair(P[9]), DoubleDouble::from_bit_pair(P[8]), ); let g5 = DoubleDouble::mul_add( z, DoubleDouble::from_bit_pair(P[11]), DoubleDouble::from_bit_pair(P[10]), ); let g6 = DoubleDouble::mul_add( z, DoubleDouble::from_bit_pair(P[13]), DoubleDouble::from_bit_pair(P[12]), ); let h0 = DoubleDouble::mul_add(z2, g1, g0); let h1 = DoubleDouble::mul_add(z2, g3, g2); let h2 = DoubleDouble::mul_add(z2, g5, g4); let q0 = DoubleDouble::mul_add(z4, h1, h0); let q1 = DoubleDouble::mul_add(z4, g6, h2); let num = DoubleDouble::mul_add(z8, q1, q0); // See numerator generation above: // poly=den; // coeffs=CoefficientList[poly,z]; // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] const Q: [(u64, u64); 14] = [ (0x0000000000000000, 0x3ff0000000000000), (0xbcfc7b886ee61417, 0x405152838f711f3c), (0xbd33f933c14e831a, 0x409576cb78cab36e), (0x3d33fb09e2c4898a, 0x40c5e8a2c7602ced), (0x3d7be430c664bf7e, 0x40e766fdc8c7638c), (0x3dac662e74cdfc0e, 0x4100276b5f47b5f1), (0x3da67d06e82a8495, 0x410f843887f8a24a), (0x3dbbf2e22fc2550a, 0x4116d04271703e08), (0xbdb2fb3aed100853, 0x4119aff4ed32b74b), (0x3dba75e7b7171c3c, 0x4116b5eb8bf386bd), (0x3dab2d8b8c1937eb, 0x410f71c38e84cb34), (0xbda4e2e8a50b7370, 0x4100ca04b0f36b94), (0xbd86ed6df34fdaf9, 0x40e9151ded4cf4b7), (0x3d6938ea702c0328, 0x40c923ee1ab270c4), ]; let g0 = DoubleDouble::mul_add( z, DoubleDouble::from_bit_pair(Q[1]), DoubleDouble::from_bit_pair(Q[0]), ); let g1 = DoubleDouble::mul_add( z, DoubleDouble::from_bit_pair(Q[3]), DoubleDouble::from_bit_pair(Q[2]), ); let g2 = DoubleDouble::mul_add( z, DoubleDouble::from_bit_pair(Q[5]), DoubleDouble::from_bit_pair(Q[4]), ); let g3 = DoubleDouble::mul_add( z, DoubleDouble::from_bit_pair(Q[7]), DoubleDouble::from_bit_pair(Q[6]), ); let g4 = DoubleDouble::mul_add( z, DoubleDouble::from_bit_pair(Q[9]), DoubleDouble::from_bit_pair(Q[8]), ); let g5 = DoubleDouble::mul_add( z, DoubleDouble::from_bit_pair(Q[11]), DoubleDouble::from_bit_pair(Q[10]), ); let g6 = DoubleDouble::mul_add( z, DoubleDouble::from_bit_pair(Q[13]), DoubleDouble::from_bit_pair(Q[12]), ); let h0 = DoubleDouble::mul_add(z2, g1, g0); let h1 = DoubleDouble::mul_add(z2, g3, g2); let h2 = DoubleDouble::mul_add(z2, g5, g4); let q0 = DoubleDouble::mul_add(z4, h1, h0); let q1 = DoubleDouble::mul_add(z4, g6, h2); let den = DoubleDouble::mul_add(z8, q1, q0); let r = DoubleDouble::div(num, den); DoubleDouble::quick_mult(r, zeta_sqrt) } #[inline] fn erf_core(x: DoubleDouble) -> DoubleDouble { // x is always positive, here, should be filtered out before the call if x.hi <= 0.0095 { // 0.0095 // for small |x| using taylor series first 3 terms // Generated by SageMath: // from mpmath import mp, erf // // mp.prec = 100 // // def inverf_series(n_terms): // from mpmath import taylor // series_erf = taylor(mp.erfinv, 0, n_terms) // return series_erf // // ser = inverf_series(10) // for i in range(1, len(ser), 2): // k = ser[i] // print("f64::from_bits(" + double_to_hex(RealField(100)(k)) + "),") let z2 = DoubleDouble::quick_mult(x, x); let p = f_fmla( z2.hi, f64::from_bits(0x3fb62847c47dda48), f64::from_bits(0x3fc053c2c0ab91c5), ); let mut r = DoubleDouble::mul_f64_add( z2, p, DoubleDouble::from_bit_pair((0xbc33ea2ef8dde075, 0x3fcdb29fb2fee5e4)), ); r = DoubleDouble::mul_add( z2, r, DoubleDouble::from_bit_pair((0xbc8618f13eb7ca89, 0x3fec5bf891b4ef6b)), ); // (rh + rl) * z = rh * z + rl*z let v = DoubleDouble::quick_mult(r, x); return v; } else if x.hi <= 0.06 { // 0.06 // for |x| < 0.06 using taylor series first 5 terms // Generated by SageMath: // from mpmath import mp, erf // // mp.prec = 100 // // def inverf_series(n_terms): // from mpmath import taylor // series_erf = taylor(mp.erfinv, 0, n_terms) // return series_erf // // ser = inverf_series(10) // for i in range(1, len(ser), 2): // k = ser[i] // print("f64::from_bits(" + double_to_hex(RealField(100)(k)) + "),") let z2 = DoubleDouble::quick_mult(x, x); let p = f_polyeval4( z2.hi, f64::from_bits(0x3fb62847c47dda48), f64::from_bits(0x3fb0a13189c6ef7a), f64::from_bits(0x3faa7c85c89bb08b), f64::from_bits(0x3fa5eeb1d488e312), ); let mut r = DoubleDouble::mul_f64_add( z2, p, DoubleDouble::from_bit_pair((0x3c2cec68daff0d80, 0x3fc053c2c0ab91c5)), ); r = DoubleDouble::mul_add( z2, r, DoubleDouble::from_bit_pair((0xbc33ea2ef8dde075, 0x3fcdb29fb2fee5e4)), ); r = DoubleDouble::mul_add( z2, r, DoubleDouble::from_bit_pair((0xbc8618f13eb7ca89, 0x3fec5bf891b4ef6b)), ); // (rh + rl) * z = rh * z + rl*z let v = DoubleDouble::quick_mult(r, x); return v; } if x.hi <= 0.75 { // |x| < 0.75 // First step rational approximant is generated, but it's ill-conditioned, thus // we're using taylor expansion to create Newton form at the point. // Generated in Wolfram Mathematica: // <75, MaxIterations->100] // num=Numerator[approx][[1]]; // den=Denominator[approx][[1]]; // poly=den; // coeffs=CoefficientList[poly,z]; // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] // x0=SetPrecision[0.5625,75]; // NumberForm[Series[num[x],{x,x0,50}], ExponentFunction->(Null&)] // coeffs=Table[SeriesCoefficient[num[x],{x,x0,k}],{k,0,9}]; // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]; const P: [(u64, u64); 5] = [ (0xbc3e06eda42202a0, 0x3f93c2fc5d00e0c8), (0xbc6eb374406b33b4, 0xbfc76fcfd022e3ff), (0xbc857822d7ffd282, 0x3fe6f8443546010a), (0x3c68269c66dfb28a, 0xbff80996754ceb79), (0x3c543dce8990a9f9, 0x3ffcf778d5ef0504), ]; let x2 = DoubleDouble::quick_mult(x, x); let vz = DoubleDouble::full_add_f64(x2, -0.5625); let ps_num = f_polyeval5( vz.hi, f64::from_bits(0xbff433be821423d0), f64::from_bits(0x3fdf15f19e9d8da4), f64::from_bits(0xbfb770b6827e0829), f64::from_bits(0x3f7a98a2980282bb), f64::from_bits(0xbf142a246fd2c07c), ); let mut num = DoubleDouble::mul_f64_add(vz, ps_num, DoubleDouble::from_bit_pair(P[4])); num = DoubleDouble::mul_add(vz, num, DoubleDouble::from_bit_pair(P[3])); num = DoubleDouble::mul_add(vz, num, DoubleDouble::from_bit_pair(P[2])); num = DoubleDouble::mul_add(vz, num, DoubleDouble::from_bit_pair(P[1])); num = DoubleDouble::mul_add(vz, num, DoubleDouble::from_bit_pair(P[0])); // Generated in Wolfram Mathematica: // <75, MaxIterations->100] // num=Numerator[approx][[1]]; // den=Denominator[approx][[1]]; // coeffs=CoefficientList[poly,z]; // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] // x0=SetPrecision[0.5625,75]; // NumberForm[Series[den[x],{x,x0,50}], ExponentFunction->(Null&)] // coeffs=Table[SeriesCoefficient[den[x],{x,x0,k}],{k,0,9}]; // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]; const Q: [(u64, u64); 5] = [ (0xbc36337f24e57cb9, 0x3f92388d5d757e3a), (0xbc63dfae43d60e0b, 0xbfc6ca7da581358c), (0xbc77656389bd0e62, 0x3fe7c82ce417b4e0), (0xbc93679667bef2f0, 0xbffad58651fd1a51), (0x3ca2c6cb9eb17fb4, 0x4001bdb67e93a242), ]; let ps_den = f_polyeval5( vz.hi, f64::from_bits(0xbffbdaeff6fbb81c), f64::from_bits(0x3fe91b12cf47da3a), f64::from_bits(0xbfc7c5d0ffb7f1da), f64::from_bits(0x3f939ada247f7609), f64::from_bits(0xbf41be65038ccfe6), ); let mut den = DoubleDouble::mul_f64_add(vz, ps_den, DoubleDouble::from_bit_pair(Q[4])); den = DoubleDouble::mul_add(vz, den, DoubleDouble::from_bit_pair(Q[3])); den = DoubleDouble::mul_add(vz, den, DoubleDouble::from_bit_pair(Q[2])); den = DoubleDouble::mul_add(vz, den, DoubleDouble::from_bit_pair(Q[1])); den = DoubleDouble::mul_add(vz, den, DoubleDouble::from_bit_pair(Q[0])); let r = DoubleDouble::div(num, den); let k = DoubleDouble::quick_mult(r, x); let err = f_fmla( k.hi, f64::from_bits(0x3c70000000000000), // 2^-56 f64::from_bits(0x3c40000000000000), // 2^-59 ); let ub = k.hi + (k.lo + err); let lb = k.hi + (k.lo - err); if ub == lb { return k; } return inverf_0p06_to_0p75(x); } let q = DoubleDouble::full_add_f64(-x, 1.0); let mut zeta = fast_log_dd(q); zeta = DoubleDouble::from_exact_add(zeta.hi, zeta.lo); zeta = -zeta; let zeta_sqrt = zeta.fast_sqrt(); let rz = zeta_sqrt.recip(); if x.hi < 0.9999 { inverf_asympt_small(rz, zeta_sqrt) } else { inverf_asympt_long(rz, zeta_sqrt) } } #[cold] fn inverfc_extra_small(x: f64) -> DoubleDouble { // Reversed order for erfinv with direct identity without subtraction. let q = x; let mut zeta = fast_log_d_to_dd(q); zeta = DoubleDouble::from_exact_add(zeta.hi, zeta.lo); zeta = -zeta; let zeta_sqrt = zeta.fast_sqrt(); let rz = zeta_sqrt.recip(); if x >= 0.0001 { inverf_asympt_small(rz, zeta_sqrt) } else { inverf_asympt_long(rz, zeta_sqrt) } } /// Complementary inverse error function pub fn f_erfcinv(x: f64) -> f64 { let ix = x.to_bits(); if ix >= 0x4000000000000000u64 || ix == 0 { // |x| == NaN, x == inf, |x| == 0, x < 0 if ix.wrapping_shl(1) == 0 { return f64::INFINITY; } if ix == 0x4000000000000000u64 { return f64::NEG_INFINITY; } return f64::NAN; // x == NaN, x == Inf, x > 2 } if x == 1. { return 0.; } // we compute erfcinv through identity // erfcinv(x) = -erfinv(1-x) static SIGN: [f64; 2] = [1.0, -1.0]; if x < 0.1 { return inverfc_extra_small(x).to_f64(); } let dx = if x > 1. { DoubleDouble::from_full_exact_sub(2., x) } else { DoubleDouble::new(0., x) }; let sign = SIGN[(x > 1.) as usize]; let mut dx = DoubleDouble::full_add_f64(-dx, 1.); dx = DoubleDouble::from_exact_add(dx.hi, dx.lo); erf_core(dx).to_f64() * sign } #[cfg(test)] mod tests { use super::*; #[test] fn test_inverfc() { assert_eq!(f_erfcinv(0.12), 1.0993909519492193); assert_eq!(f_erfcinv(1.0000000000027623e-13), 5.261512368864527); assert_eq!(f_erfcinv(1.0001200000182189), -0.00010634724760131264); assert_eq!(f_erfcinv(0.7001200000182189), 0.2723481758403576); assert_eq!(f_erfcinv(1.5231200000182189), -0.502985998867995); assert_eq!(f_erfcinv(1.99545434324323243), -2.0064739778442213); assert_eq!(f_erfcinv(1.), 0.); assert!(f_erfcinv(2.05).is_nan()); assert!(f_erfcinv(-0.01).is_nan()); assert!(f_erfcinv(f64::NAN).is_nan()); assert!(f_erfcinv(f64::NEG_INFINITY).is_nan()); assert!(f_erfcinv(f64::INFINITY).is_nan()); } }