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:
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,