zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit 4cd396fc32cdc4488ecd9a36f2067b61142d727e (tree)
parent 5e8a9ed9e87f9152e40c7493b57b0622ceb20f29
Author: Christophe Delage <christ@ophe.net>
Date:   Fri, 29 May 2026 13:53:34 +0200

Finish the code sharing between logq, log2q and log10q

Diffstat:
Mlib/compiler_rt/log.zig | 18++----------------
Mlib/compiler_rt/log10.zig | 116++++++++++++++++++++-----------------------------------------------------------
Mlib/compiler_rt/log2.zig | 119++++++++++++++++++++-----------------------------------------------------------
Mlib/compiler_rt/log_f128.zig | 34++++++++++++++++++++++++++++++++++
4 files changed, 95 insertions(+), 192 deletions(-)

diff --git a/lib/compiler_rt/log.zig b/lib/compiler_rt/log.zig @@ -452,21 +452,8 @@ pub fn __logx(a: f80) callconv(.c) f80 { pub fn logq(x: f128) callconv(.c) f128 { const impl = @import("log_f128.zig"); - if (!math.isFinite(x)) { - if (math.isNan(x)) { - if (math.isSignalNan(x)) math.raiseInvalid(); - return math.nan(f128); - } - if (math.isPositiveInf(x)) return x; - } - if (x <= 0.0) { - if (x >= 0.0) { - math.raiseDivByZero(); - return -math.inf(f128); - } - math.raiseInvalid(); - return math.nan(f128); - } + if (impl.specialCases(x)) |y| + return y; if (impl.Proc2.lo < x and x < impl.Proc2.hi) { // Polynomial approximation of log((1 + u / 2) / (1 - u / 2)) @@ -634,7 +621,6 @@ pub fn logq(x: f128) callconv(.c) f128 { .{ .hi = 0x1.60e32f44788d8ca7c895a0b5p-1, .lo = -0x1.3557995d063914a66aa81ead3fdbp-101 }, .{ .hi = 0x1.62e42fefa39ef35793c7673p-1, .lo = 0x1.f97b57a079a193394c5b16c5068cp-103 }, }; - return impl.proc1(.{ .poly = poly, .tab = tab }, x); } diff --git a/lib/compiler_rt/log10.zig b/lib/compiler_rt/log10.zig @@ -183,101 +183,47 @@ pub fn __log10x(a: f80) callconv(.c) f80 { /// Accuracy on 10 million random numbers near x = 1 (testing the proc2 case): /// <= 0.5 ulp: 99.96%, worst case <= 0.565 ulp pub fn log10q(x: f128) callconv(.c) f128 { - if (!math.isFinite(x)) { - if (math.isNan(x)) { - if (math.isSignalNan(x)) math.raiseInvalid(); - return math.nan(f128); - } - if (math.isPositiveInf(x)) return x; - } - if (x <= 0.0) { - if (x >= 0.0) { - math.raiseDivByZero(); - return -math.inf(f128); - } - math.raiseInvalid(); - return math.nan(f128); - } - const bitsize = 7; - const size = 1 << bitsize; - - // exp10(-1 / 16) rounded down - const proc2_lo: f128 = 0.939413062813475786119710824622305; - // exp10(1 / 16) rounded up - const proc2_hi: f128 = 1.0644944589178594295633905946428897; - if (proc2_lo < x and x < proc2_hi) { - const f = x - 1.0; - const g = 1 / (2 + f); - const u = 2 * f * g; - const v = u * u; - const uv = u * v; - const v64: f64 = @floatCast(v); + const impl = @import("log_f128.zig"); + + if (impl.specialCases(x)) |y| + return y; + if (impl.Proc2.lo < x and x < impl.Proc2.hi) { // Polynomial approximation of log10((1 + u / 2) / (1 - u / 2)) // in [2 * a / (2 + a), 2 * b / (2 + b)] // where a = exp(-1 / 16) - 1 and b = exp(1 / 16) - 1 - const p19 = 8.757839894876785986064901881670424e-8; - const p17 = 3.898090687230025454479255130305971e-7 + v64 * p19; - const p15 = 1.7671487851436387503930903139882346e-6 + v64 * p17; - const p13 = 8.156071249646061672592451832809541e-6 + v * p15; - const p11 = 3.855597318033137224139238937796866e-5 + v * p13; - const p9 = 1.8849586888161971679466375197398104e-4 + v * p11; - const p7 = 9.694073256769014010070232880860484e-4 + v * p9; - const p5 = 5.428681023790647845639111475407614e-3 + v * p7; - const p3 = 3.619120682527098563759407657638483e-2; - - const q_hi = uv * p3; - const q_lo = uv * v * p5; - - const f_hi: f128 = @as(f64, @floatCast(f)); - const f_lo: f128 = f - f_hi; - - const u_hi: f128 = @as(f64, @floatCast(u)); - const u_lo: f128 = ((2 * (f - u_hi) - u_hi * f_hi) - u_hi * f_lo) * g; - - const log10e_hi: f128 = 0x1.bcb7b1526e50ep-2; - const log10e_lo: f128 = 0x1.95355baaafad33dc323ee3460246p-57; - // t = u / log(10) - const t_hi = u_hi * log10e_hi; - const t_lo = u_lo * log10e_hi + u * log10e_lo; - - // y = t + q - const y_hi = t_hi + q_hi; - const y_lo = t_lo + (t_hi - y_hi + q_hi) + q_lo; - - return y_hi + y_lo; + const poly: impl.Proc2.Poly = .{ + .b1_hi = 0x1.bcb7b1526e50ep-2, + .b1_lo = 0x1.95355baaafad33dc323ee3460246p-57, + .b3 = 3.619120682527098563759407657638483e-2, + .b5 = 5.428681023790647845639111475407614e-3, + .b7 = 9.694073256769014010070232880860484e-4, + .b9 = 1.8849586888161971679466375197398104e-4, + .b11 = 3.855597318033137224139238937796866e-5, + .b13 = 8.156071249646061672592451832809541e-6, + .b15 = 1.7671487851436387503930903139882346e-6, + .b17 = 3.898090687230025454479255130305971e-7, + .b19 = 8.757839894876785986064901881670424e-8, + }; + return impl.proc2(.{ .poly = poly }, x); } - const ym = @import("log.zig").frexp2(x); - const y = ym.significand; - const m = ym.exponent; - - const F0 = @round(math.ldexp(y, bitsize)); - const j0: usize = @intFromFloat(F0); - const j = j0 - size; - const F = math.ldexp(F0, -bitsize); - const f = y - F; - - const u = (f + f) / (y + F); - const v = u * u; - const v64: f64 = @floatCast(v); - // Polynomial approximation of log10(1 + 2 * u / (2 - u)) // in [-(2 * fmax) / (2 + fmax), (2 * fmax) / (2 - fmax)] // where fmax = 0.5 / size - const p11 = 3.8556341504143175800053507804546873e-5; - const p9 = 1.884958688754320118955531917460363e-4 + v64 * p11; - const p7 = 9.694073256769014481942040422515466e-4 + v * p9; - const p5 = 5.428681023790647845638954444458386e-3 + v * p7; - const p3 = 3.619120682527098563759407657655348e-2 + v * p5; - const p1 = 0.4342944819032518276511289189166051; - - const q = u * v * p3; + const poly: impl.Proc1.Poly = .{ + .a1 = 0.4342944819032518276511289189166051, + .a3 = 3.619120682527098563759407657655348e-2, + .a5 = 5.428681023790647845638954444458386e-3, + .a7 = 9.694073256769014481942040422515466e-4, + .a9 = 1.884958688754320118955531917460363e-4, + .a11 = 3.8556341504143175800053507804546873e-5, + }; // log1p_tab[j].hi = 2^-n * round-to-integer(2^n * l) // log1p_tab[j].lo = round-to-nearest-f128(l - log1p_tab[j].hi) // where n = 97 and l = log10(1 + j / size) - const log1p_tab = [size + 1]struct { hi: f128, lo: f128 }{ + const tab = [impl.size + 1]impl.Proc1.HiLo{ .{ .hi = 0, .lo = 0 }, .{ .hi = 0x1.bafd47221ed2665c1ba949p-9, .lo = -0x1.eb6f20a90ad48515635f3b8a1d22p-104 }, .{ .hi = 0x1.b9476a4fcd10ed89b5a417p-8, .lo = 0x1.0b153c94bfd2527c3dce31e5e226p-100 }, @@ -408,11 +354,7 @@ pub fn log10q(x: f128) callconv(.c) f128 { .{ .hi = 0x1.32839e681fc6236e91f3dacap-2, .lo = -0x1.451bc31fd56e57af018a8d364cb8p-99 }, .{ .hi = 0x1.34413509f79fef311f12b358p-2, .lo = 0x1.6f922f04d5a618a87a3e69314bcep-102 }, }; - const xm: f128 = @floatFromInt(m); - const l_hi = xm * log1p_tab[128].hi + log1p_tab[j].hi; - const l_lo = xm * log1p_tab[128].lo + log1p_tab[j].lo; - - return l_hi + (u * p1 + (q + l_lo)); + return impl.proc1(.{ .poly = poly, .tab = tab }, x); } pub fn log10l(x: c_longdouble) callconv(.c) c_longdouble { diff --git a/lib/compiler_rt/log2.zig b/lib/compiler_rt/log2.zig @@ -176,101 +176,46 @@ pub fn __log2x(a: f80) callconv(.c) f80 { /// Accuracy on 10 million random numbers near x = 1 (testing the proc2 case): /// <= 0.5 ulp: 99.86%, worst case <= 0.546 ulp pub fn log2q(x: f128) callconv(.c) f128 { - const bitsize = 7; - const size = 1 << bitsize; - if (!math.isFinite(x)) { - if (math.isNan(x)) { - if (math.isSignalNan(x)) math.raiseInvalid(); - return math.nan(f128); - } - if (math.isPositiveInf(x)) return x; - } - if (x <= 0.0) { - if (x >= 0.0) { - math.raiseDivByZero(); - return -math.inf(f128); - } - math.raiseInvalid(); - return math.nan(f128); - } + const impl = @import("log_f128.zig"); - // exp(-1 / 16) rounded down - const proc2_lo: f128 = 0.939413062813475786119710824622305; - // exp(1 / 16) rounded up - const proc2_hi: f128 = 1.0644944589178594295633905946428897; - if (proc2_lo < x and x < proc2_hi) { - const f = x - 1.0; - const g = 1 / (2 + f); - const u = 2 * f * g; - const v = u * u; - const uv = u * v; - const v64: f64 = @floatCast(v); + if (impl.specialCases(x)) |y| + return y; + if (impl.Proc2.lo < x and x < impl.Proc2.hi) { // Polynomial approximation of log2((1 + u / 2) / (1 - u / 2)) // in [2 * a / (2 + a), 2 * b / (2 + b)] // where a = exp(-1 / 16) - 1 and b = exp(1 / 16) - 1 - const p19 = 2.909291439731657940692470637735429e-7; - const p17 = 1.294917697032820750200161813672143e-6 + v64 * p19; - const p15 = 5.870341197214724685339102193694838e-6 + v64 * p17; - const p13 = 2.7093882228102330360125035037716968e-5 + v * p15; - const p11 = 1.280801705334664325639770412440281e-4 + v * p13; - const p9 = 6.261697226080570342191671010883619e-4 + v * p11; - const p7 = 3.2203014305557218914285331735164364e-3 + v * p9; - const p5 = 1.8033688011112042591999058475816515e-2 + v * p7; - const p3 = 0.12022458674074695061332705675016125; - - const q_hi = uv * p3; - const q_lo = uv * v * p5; - - const f_hi: f128 = @as(f64, @floatCast(f)); - const f_lo: f128 = f - f_hi; - - const u_hi: f128 = @as(f64, @floatCast(u)); - const u_lo: f128 = ((2 * (f - u_hi) - u_hi * f_hi) - u_hi * f_lo) * g; - - // t = u / log(2) - const log2e_hi: f128 = 0x1.71547652b82fep0; - const log2e_lo: f128 = 0x1.777d0ffda0d23a7d11d6aef551bbp-56; - const t_hi = u_hi * log2e_hi; - const t_lo = u_lo * log2e_hi + u * log2e_lo; - - // y = t + q - const y_hi = t_hi + q_hi; - const y_lo = t_lo + (t_hi - y_hi + q_hi) + q_lo; - - return y_hi + y_lo; + const poly: impl.Proc2.Poly = .{ + .b1_hi = 0x1.71547652b82fep0, + .b1_lo = 0x1.777d0ffda0d23a7d11d6aef551bbp-56, + .b3 = 0.12022458674074695061332705675016125, + .b5 = 1.8033688011112042591999058475816515e-2, + .b7 = 3.2203014305557218914285331735164364e-3, + .b9 = 6.261697226080570342191671010883619e-4, + .b11 = 1.280801705334664325639770412440281e-4, + .b13 = 2.7093882228102330360125035037716968e-5, + .b15 = 5.870341197214724685339102193694838e-6, + .b17 = 1.294917697032820750200161813672143e-6, + .b19 = 2.909291439731657940692470637735429e-7, + }; + return impl.proc2(.{ .poly = poly }, x); } - const ym = @import("log.zig").frexp2(x); - const y = ym.significand; - const m = ym.exponent; - - const F0 = @round(math.ldexp(y, bitsize)); - const j0: usize = @intFromFloat(F0); - const j = j0 - size; - const F = math.ldexp(F0, -bitsize); - const f = y - F; - - const u = (f + f) / (y + F); - const v = u * u; - const v64: f64 = @floatCast(v); - // Polynomial approximation of log2(1 + 2 * u / (2 - u)) // in [-(2 * fmax) / (2 + fmax), (2 * fmax) / (2 - fmax)] // where fmax = 0.5 / size - const p11 = 1.280813940786848788109850061222256e-4; - const p9 = 6.261697225875019234719395591078697e-4 + v64 * p11; - const p7 = 3.22030143055572204818095463930704e-3 + v * p9; - const p5 = 1.8033688011112042591998536830294507e-2 + v * p7; - const p3 = 0.12022458674074695061332705675072149 + v * p5; - const p1 = 1.442695040888963407359924681001892; - - const q = u * v * p3; - - // log1p_tab[j].hi = 2^-n * round-to-integer(2^n * l) - // log1p_tab[j].lo = round-to-nearest-f128(l - log1p_tab[j].hi) + const poly: impl.Proc1.Poly = .{ + .a1 = 1.442695040888963407359924681001892, + .a3 = 0.12022458674074695061332705675072149, + .a5 = 1.8033688011112042591998536830294507e-2, + .a7 = 3.22030143055572204818095463930704e-3, + .a9 = 6.261697225875019234719395591078697e-4, + .a11 = 1.280813940786848788109850061222256e-4, + }; + // tab[j].hi = 2^-n * round-to-integer(2^n * l) + // tab[j].lo = round-to-nearest-f128(l - tab[j].hi) // where n = 97 and l = log2(1 + j / size) - const log1p_tab = [size + 1]struct { hi: f128, lo: f128 }{ + const tab = [impl.size + 1]impl.Proc1.HiLo{ .{ .hi = 0, .lo = 0 }, .{ .hi = 0x1.6fe50b6ef08517f8e37bp-7, .lo = 0x1.794f4441ccdf648f265a41e57d75p-99 }, .{ .hi = 0x1.6e79685c2d2298a6e27e212p-6, .lo = -0x1.fbd41ae7d5a2434912ad3fe21cfbp-100 }, @@ -401,11 +346,7 @@ pub fn log2q(x: f128) callconv(.c) f128 { .{ .hi = 0x1.fd1be4c7f2af942b221ce0d1p-1, .lo = 0x1.a275c854f5bb9732fae5130be48bp-104 }, .{ .hi = 0x1p0, .lo = 0 }, }; - const xm: f128 = @floatFromInt(m); - const l_hi = xm * log1p_tab[128].hi + log1p_tab[j].hi; - const l_lo = xm * log1p_tab[128].lo + log1p_tab[j].lo; - - return l_hi + (u * p1 + (q + l_lo)); + return impl.proc1(.{ .poly = poly, .tab = tab }, x); } pub fn log2l(x: c_longdouble) callconv(.c) c_longdouble { diff --git a/lib/compiler_rt/log_f128.zig b/lib/compiler_rt/log_f128.zig @@ -1,9 +1,43 @@ +/// Implementation of "Table-driven implementation of the logarithm function in IEEE floating-point arithmetic" +/// by PTP Tang in ACM Transactions on Mathematical Software (TOMS), 1990 +/// +/// https://dl.acm.org/doi/pdf/10.1145/98267.98294 +/// +/// Adapted to work for f128 and bases 2 and 10 by Christophe Delage. +/// +/// This file contains the code shared between logq, log2q and log10q. +const log_f128 = @This(); + const std = @import("std"); const math = std.math; pub const log2size = 7; pub const size = 1 << log2size; +/// Filter out special cases for log in bases {e,2,10}. +/// +/// If x is finite and positive, returns null. +/// Returns the appropriate NaN or inf otherwise. +pub fn specialCases(x: f128) ?f128 { + if (!math.isFinite(x)) { + if (math.isNan(x)) { + if (math.isSignalNan(x)) math.raiseInvalid(); + return math.nan(f128); + } + if (math.isPositiveInf(x)) return x; + } + if (x <= 0.0) { + if (x >= 0.0) { + math.raiseDivByZero(); + return -math.inf(f128); + } + math.raiseInvalid(); + return math.nan(f128); + } + + return null; +} + pub const Proc1 = struct { pub const Poly = struct { a1: f128,