authorgravatar for christ@ophe.netChristophe Delage <christ@ophe.net> 2026-05-29 13:11:42+02:00
committergravatar for christ@ophe.netChristophe Delage <christ@ophe.net> 2026-05-29 13:11:42+02:00
log5e8a9ed9e87f9152e40c7493b57b0622ceb20f29
tree9cb68fe1d9ae651ae2a3c21aa4ad8d20c44413f6
parent9ed970090c1fe6b7d2d2f0eef1a59343e7b9a67d

Scaffolding for the code sharing between logq, log2q and log10q


2 files changed, 167 insertions(+), 64 deletions(-)

lib/compiler_rt/log.zig+28-64
......@@ -450,8 +450,7 @@ pub fn __logx(a: f80) callconv(.c) f80 {
450450/// Accuracy on 10 million random numbers near x = 1 (testing the proc2 case):
451451/// <= 0.5 ulp: 99.96%, worst case <= 0.528 ulp
452452pub fn logq(x: f128) callconv(.c) f128 {
453 const bitsize = 7;
454 const size = 1 << bitsize;
453 const impl = @import("log_f128.zig");
455454
456455 if (!math.isFinite(x)) {
457456 if (math.isNan(x)) {
......@@ -469,74 +468,42 @@ pub fn logq(x: f128) callconv(.c) f128 {
469468 return math.nan(f128);
470469 }
471470
472 // exp(-1 / 16) rounded down
473 const proc2_lo: f128 = 0.939413062813475786119710824622305;
474 // exp(1 / 16) rounded up
475 const proc2_hi: f128 = 1.0644944589178594295633905946428897;
476 if (proc2_lo < x and x < proc2_hi) {
477 const f = x - 1.0;
478
479 const g = 1 / (2 + f);
480 const u = 2 * f * g;
481 const v = u * u;
482 const uv = u * v;
483 const v64: f64 = @floatCast(v);
484
471 if (impl.Proc2.lo < x and x < impl.Proc2.hi) {
485472 // Polynomial approximation of log((1 + u / 2) / (1 - u / 2))
486473 // in [2 * a / (2 + a), 2 * b / (2 + b)]
487474 // where a = exp(-1 / 16) - 1 and b = exp(1 / 16) - 1
488 const p19 = 2.0165671588771827537210411918018159e-7;
489 const p17 = 8.97568550755477160981619052649713e-7 + v64 * p19;
490 const p15 = 4.069010449774280288178309893970754e-6 + v64 * p17;
491 const p13 = 1.8780048076832339308077858301484127e-5 + v * p15;
492 const p11 = 8.87784090909092440759545734146088e-5 + v * p13;
493 const p9 = 4.340277777777777776216500817402857e-4 + v * p11;
494 const p7 = 2.2321428571428571428572328745789477e-3 + v * p9;
495 const p5 = 1.249999999999999999999999997455655e-2 + v * p7;
496 const p3 = 8.333333333333333333333333333333581e-2;
497
498 const q_hi = uv * p3;
499 const q_lo = uv * v * p5;
500 const q = q_hi + q_lo;
501
502 const fa: f128 = @as(f64, @floatCast(f));
503 const ua: f128 = @as(f64, @floatCast(u));
504
505 const fb: f128 = f - fa;
506 const ub: f128 = ((2 * (f - ua) - ua * fa) - ua * fb) * g;
507
508 return ua + (ub + q);
475 const poly: impl.Proc2.Poly = .{
476 .b1_hi = 1.0,
477 .b1_lo = 0.0,
478 .b3 = 8.333333333333333333333333333333581e-2,
479 .b5 = 1.249999999999999999999999997455655e-2,
480 .b7 = 2.2321428571428571428572328745789477e-3,
481 .b9 = 4.340277777777777776216500817402857e-4,
482 .b11 = 8.87784090909092440759545734146088e-5,
483 .b13 = 1.8780048076832339308077858301484127e-5,
484 .b15 = 4.069010449774280288178309893970754e-6,
485 .b17 = 8.97568550755477160981619052649713e-7,
486 .b19 = 2.0165671588771827537210411918018159e-7,
487 };
488 return impl.proc2(.{ .poly = poly }, x);
509489 }
510490
511 const ym = frexp2(x);
512 const y = ym.significand;
513 const m = ym.exponent;
514
515 const F0 = @round(math.ldexp(y, bitsize));
516 const j0: usize = @intFromFloat(F0);
517 const j = j0 - size;
518 const F = math.ldexp(F0, -bitsize);
519 const f = y - F;
520
521 const u = (f + f) / (y + F);
522 const v = u * u;
523 const v64: f64 = @floatCast(v);
524
525491 // Polynomial approximation of log(1 + 2 * u / (2 - u))
526492 // in [-(2 * fmax) / (2 + fmax), (2 * fmax) / (2 - fmax)]
527493 // where fmax = 0.5 / size
528 const p11 = 8.877925718782769769445565656611838e-5;
529 const p9 = 4.340277777635300605611118803507141e-4 + v64 * p11;
530 const p7 = 2.2321428571428572515097318595359542e-3 + v * p9;
531 const p5 = 1.249999999999999999999963839743372e-2 + v * p7;
532 const p3 = 8.333333333333333333333333333372414e-2 + v * p5;
533
534 const q = u * v * p3;
494 const poly: impl.Proc1.Poly = .{
495 .a1 = 1.0,
496 .a3 = 8.333333333333333333333333333372414e-2,
497 .a5 = 1.249999999999999999999963839743372e-2,
498 .a7 = 2.2321428571428572515097318595359542e-3,
499 .a9 = 4.340277777635300605611118803507141e-4,
500 .a11 = 8.877925718782769769445565656611838e-5,
501 };
535502
536 // log1p_tab[j].hi = 2^-n * round-to-integer(2^n * l)
537 // log1p_tab[j].lo = round-to-nearest-f128(l - log1p_tab[j].hi)
503 // tab[j].hi = 2^-n * round-to-integer(2^n * l)
504 // tab[j].lo = round-to-nearest-f128(l - tab[j].hi)
538505 // where n = 97 and l = log(1 + j / size)
539 const log1p_tab = [size + 1]struct { hi: f128, lo: f128 }{
506 const tab = [impl.size + 1]impl.Proc1.HiLo{
540507 .{ .hi = 0, .lo = 0 },
541508 .{ .hi = 0x1.fe02a6b106788fc3769039p-8, .lo = 0x1.dc282d2b3db2c3ef9a073a876702p-100 },
542509 .{ .hi = 0x1.fc0a8b0fc03e3cf9eda74d4p-7, .lo = -0x1.0a8552414fc416fc223acca2ebfp-100 },
......@@ -667,11 +634,8 @@ pub fn logq(x: f128) callconv(.c) f128 {
667634 .{ .hi = 0x1.60e32f44788d8ca7c895a0b5p-1, .lo = -0x1.3557995d063914a66aa81ead3fdbp-101 },
668635 .{ .hi = 0x1.62e42fefa39ef35793c7673p-1, .lo = 0x1.f97b57a079a193394c5b16c5068cp-103 },
669636 };
670 const xm: f128 = @floatFromInt(m);
671 const l_hi = xm * log1p_tab[128].hi + log1p_tab[j].hi;
672 const l_lo = xm * log1p_tab[128].lo + log1p_tab[j].lo;
673637
674 return l_hi + (u + (q + l_lo));
638 return impl.proc1(.{ .poly = poly, .tab = tab }, x);
675639}
676640
677641pub fn logl(x: c_longdouble) callconv(.c) c_longdouble {
lib/compiler_rt/log_f128.zig created+139
......@@ -0,0 +1,139 @@
1const std = @import("std");
2const math = std.math;
3
4pub const log2size = 7;
5pub const size = 1 << log2size;
6
7pub const Proc1 = struct {
8 pub const Poly = struct {
9 a1: f128,
10 a3: f128,
11 a5: f128,
12 a7: f128,
13 a9: f64,
14 a11: f64,
15 };
16 pub const HiLo = struct { hi: f128, lo: f128 };
17 poly: Poly,
18 tab: [size + 1]HiLo,
19};
20
21pub fn proc1(comptime p: Proc1, x: f128) f128 {
22 const ym = frexp2(x);
23 const y = ym.significand;
24 const m = ym.exponent;
25
26 const F0 = @round(math.ldexp(y, log2size));
27 const j0: usize = @intFromFloat(F0);
28 const j = j0 - size;
29 const F = math.ldexp(F0, -log2size);
30 const f = y - F;
31
32 const u = (f + f) / (y + F);
33 const v = u * u;
34 const v64: f64 = @floatCast(v);
35
36 const p9 = p.poly.a9 + v64 * p.poly.a11;
37 const p7 = p.poly.a7 + v * p9;
38 const p5 = p.poly.a5 + v * p7;
39 const p3 = p.poly.a3 + v * p5;
40
41 const q = u * v * p3;
42
43 const xm: f128 = @floatFromInt(m);
44 const l_hi = xm * p.tab[128].hi + p.tab[j].hi;
45 const l_lo = xm * p.tab[128].lo + p.tab[j].lo;
46
47 if (comptime p.poly.a1 == 1.0)
48 return l_hi + (u + (q + l_lo))
49 else
50 return l_hi + (u * p.poly.a1 + (q + l_lo));
51}
52
53pub const Proc2 = struct {
54 // exp(-1 / 16) rounded down
55 pub const lo: f128 = 0.939413062813475786119710824622305;
56 // exp(1 / 16) rounded up
57 pub const hi: f128 = 1.0644944589178594295633905946428897;
58
59 pub const Poly = struct {
60 b1_hi: f128,
61 b1_lo: f128,
62 b3: f128,
63 b5: f128,
64 b7: f128,
65 b9: f128,
66 b11: f128,
67 b13: f128,
68 b15: f64,
69 b17: f64,
70 b19: f64,
71 };
72
73 poly: Poly,
74};
75
76pub fn proc2(comptime p: Proc2, x: f128) f128 {
77 std.debug.assert(Proc2.lo < x and x < Proc2.hi);
78
79 const f = x - 1.0;
80 const g = 1 / (2 + f);
81 const u = 2 * f * g;
82 const v = u * u;
83 const uv = u * v;
84 const v64: f64 = @floatCast(v);
85
86 const p17 = p.poly.b17 + v64 * p.poly.b19;
87 const p15 = p.poly.b15 + v64 * p17;
88 const p13 = p.poly.b13 + v * p15;
89 const p11 = p.poly.b11 + v * p13;
90 const p9 = p.poly.b9 + v * p11;
91 const p7 = p.poly.b7 + v * p9;
92 const p5 = p.poly.b5 + v * p7;
93
94 const q_hi = uv * p.poly.b3;
95 const q_lo = uv * v * p5;
96
97 const f_hi: f128 = @as(f64, @floatCast(f));
98 const f_lo = f - f_hi;
99
100 const u_hi: f128 = @as(f64, @floatCast(u));
101 const u_lo = ((2 * (f - u_hi) - u_hi * f_hi) - u_hi * f_lo) * g;
102
103 if (comptime p.poly.b1_hi == 1.0 and p.poly.b1_lo == 0.0)
104 return u_hi + (u_lo + (q_hi + q_lo));
105
106 // t = u * p.poly.b1
107 const t_hi = u_hi * p.poly.b1_hi;
108 const t_lo = u_lo * p.poly.b1_hi + u * p.poly.b1_lo;
109
110 // y = t + q
111 const y_hi = t_hi + q_hi;
112 const y_lo = t_lo + (t_hi - y_hi + q_hi) + q_lo;
113
114 return y_hi + y_lo;
115}
116
117/// Returns (f, k) such that x = f * 2^k and f in [1,2).
118/// Asserts that x is finite and positive.
119pub fn frexp2(x: f128) math.Frexp(f128) {
120 std.debug.assert(math.isFinite(x));
121 std.debug.assert(x > 0.0);
122
123 const bits: u128 = @bitCast(x);
124 const uexp: i32 = @intCast(bits >> 112);
125
126 std.debug.assert(uexp >= 0);
127
128 if (uexp == 0) {
129 const shift: u7 = @intCast(@clz(bits) - 15);
130
131 const exp = -@as(i32, shift) - 0x3ffe;
132 const frac: f128 = @bitCast((bits << shift) | (0x3fff << 112));
133 return .{ .significand = frac, .exponent = exp };
134 }
135
136 const exp = uexp - 0x3fff;
137 const frac: f128 = @bitCast((0x3fff << 112) | ((bits << 16) >> 16));
138 return .{ .significand = frac, .exponent = exp };
139}