authorgravatar for hi@mihaelm.commihael <hi@mihaelm.com> 2026-03-26 21:43:55+01:00
committergravatar for hi@mihaelm.commihael <hi@mihaelm.com> 2026-04-02 23:54:20+02:00
log9a53be5fb444873ee561eb54a0ea1aaeaaad844d
tree996d4266127415ce1eb7ebe04204b1f5e8693032
parenta6d6af947d09fbd52662c96c6f0a12510ef15f70
signaturebadge-check Signed by SSH key SHA256:aoFoShdYLdrqMichqKXSSieTKUfACUIDJHsKc4V2tQg

`libzigc/math`: Implement more precise `sincosl` in `compiler_rt`

The changes were tested by running: ```bash $ ./build/stage3/bin/zig build -p stage4 -Denable-llvm -Dno-lib $ stage4/bin/zig build test-libc -Dlibc-test-path=<LIBC-TEST-PATH> -Dtest-filter=sincosl -fqemu -fwasmtime --summary line Build Summary: 369/369 steps succeeded ``` Additionally, I've added unit tests for `sincos`.

4 files changed, 223 insertions(+), 119 deletions(-)

lib/compiler_rt/sincos.zig+222-52
......@@ -3,9 +3,13 @@ const builtin = @import("builtin");
33const arch = builtin.cpu.arch;
44const math = std.math;
55const mem = std.mem;
6const expect = std.testing.expect;
7const expectApproxEqAbs = std.testing.expectApproxEqAbs;
68const trig = @import("trig.zig");
79const rem_pio2 = @import("rem_pio2.zig").rem_pio2;
810const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f;
11const rem_pio2l = @import("rem_pio2l.zig").rem_pio2l;
12const utils = @import("math_utils.zig");
913const compiler_rt = @import("../compiler_rt.zig");
1014const symbol = compiler_rt.symbol;
1115
......@@ -188,50 +192,12 @@ pub fn sincos(x: f64, r_sin: *f64, r_cos: *f64) callconv(.c) void {
188192 }
189193}
190194
191pub fn __sincosx(x: f80, r_sin: *f80, r_cos: *f80) callconv(.c) void {
192 // TODO: more efficient implementation
193 //return sincos_generic(f80, x, r_sin, r_cos);
194 var big_sin: f128 = undefined;
195 var big_cos: f128 = undefined;
196 sincosq(x, &big_sin, &big_cos);
197 r_sin.* = @as(f80, @floatCast(big_sin));
198 r_cos.* = @as(f80, @floatCast(big_cos));
199}
200
201pub fn sincosq(x: f128, r_sin: *f128, r_cos: *f128) callconv(.c) void {
202 // TODO: more correct implementation
203 //return sincos_generic(f128, x, r_sin, r_cos);
204 var small_sin: f64 = undefined;
205 var small_cos: f64 = undefined;
206 sincos(@as(f64, @floatCast(x)), &small_sin, &small_cos);
207 r_sin.* = small_sin;
208 r_cos.* = small_cos;
209}
210
211pub fn sincosl(x: c_longdouble, r_sin: *c_longdouble, r_cos: *c_longdouble) callconv(.c) void {
212 switch (@typeInfo(c_longdouble).float.bits) {
213 16 => return __sincosh(x, r_sin, r_cos),
214 32 => return sincosf(x, r_sin, r_cos),
215 64 => return sincos(x, r_sin, r_cos),
216 80 => return __sincosx(x, r_sin, r_cos),
217 128 => return sincosq(x, r_sin, r_cos),
218 else => @compileError("unreachable"),
195fn sincoslGeneric(comptime T: type, x: T, r_sin: *T, r_cos: *T) void {
196 if (T != f80 and T != f128) {
197 @compileError("`sincoslGeneric` implemented only for `f80` and `f128`, got: " ++ @typeName(T));
219198 }
220}
221
222pub const rem_pio2_generic = @compileError("TODO");
223
224/// Ported from musl sincosl.c. Needs the following dependencies to be complete:
225/// * rem_pio2_generic ported from __rem_pio2l.c
226/// * trig.sin_generic ported from __sinl.c
227/// * trig.cos_generic ported from __cosl.c
228inline fn sincos_generic(comptime F: type, x: F, r_sin: *F, r_cos: *F) void {
229 const sc1pio4: F = 1.0 * math.pi / 4.0;
230 const bits = @typeInfo(F).float.bits;
231 const I = std.meta.Int(.unsigned, bits);
232 const ix = @as(I, @bitCast(x)) & (math.maxInt(I) >> 1);
233 const se: u16 = @truncate(ix >> (bits - 16));
234199
200 const se = utils.ldSignExponent(x) & 0x7fff;
235201 if (se == 0x7fff) {
236202 const result = x - x;
237203 r_sin.* = result;
......@@ -239,26 +205,26 @@ inline fn sincos_generic(comptime F: type, x: F, r_sin: *F, r_cos: *F) void {
239205 return;
240206 }
241207
242 if (@as(F, @bitCast(ix)) < sc1pio4) {
243 if (se < 0x3fff - math.floatFractionalBits(F) - 1) {
208 if (@abs(x) < utils.pi_4) {
209 if (se < 0x3fff - math.floatMantissaBits(T)) {
244210 // raise underflow if subnormal
245 if (se == 0) {
246 if (compiler_rt.want_float_exceptions) mem.doNotOptimizeAway(x * 0x1p-120);
211 if (compiler_rt.want_float_exceptions and se == 0) {
212 mem.doNotOptimizeAway(x * 0x1p-120);
247213 }
248214 r_sin.* = x;
249215 // raise inexact if x!=0
250216 r_cos.* = 1.0 + x;
251217 return;
252218 }
253 r_sin.* = trig.sin_generic(F, x, 0, 0);
254 r_cos.* = trig.cos_generic(F, x, 0);
219 r_sin.* = trig.__sinl(T, x, 0.0, 0);
220 r_cos.* = trig.__cosl(T, x, 0.0);
255221 return;
256222 }
257223
258 var y: [2]F = undefined;
259 const n = rem_pio2_generic(F, x, &y);
260 const s = trig.sin_generic(F, y[0], y[1], 1);
261 const c = trig.cos_generic(F, y[0], y[1]);
224 var y: [2]T = undefined;
225 const n = rem_pio2l(T, x, &y);
226 const s = trig.__sinl(T, y[0], y[1], 1);
227 const c = trig.__cosl(T, y[0], y[1]);
262228 switch (n & 3) {
263229 0 => {
264230 r_sin.* = s;
......@@ -278,3 +244,207 @@ inline fn sincos_generic(comptime F: type, x: F, r_sin: *F, r_cos: *F) void {
278244 },
279245 }
280246}
247
248pub fn __sincosx(x: f80, r_sin: *f80, r_cos: *f80) callconv(.c) void {
249 return sincoslGeneric(f80, x, r_sin, r_cos);
250}
251
252pub fn sincosq(x: f128, r_sin: *f128, r_cos: *f128) callconv(.c) void {
253 return sincoslGeneric(f128, x, r_sin, r_cos);
254}
255
256pub fn sincosl(x: c_longdouble, r_sin: *c_longdouble, r_cos: *c_longdouble) callconv(.c) void {
257 switch (@typeInfo(c_longdouble).float.bits) {
258 16 => return __sincosh(x, r_sin, r_cos),
259 32 => return sincosf(x, r_sin, r_cos),
260 64 => return sincos(x, r_sin, r_cos),
261 80 => return __sincosx(x, r_sin, r_cos),
262 128 => return sincosq(x, r_sin, r_cos),
263 else => @compileError("unreachable"),
264 }
265}
266
267fn testSincosSpecial(comptime T: type) !void {
268 const f = switch (T) {
269 f32 => sincosf,
270 f64 => sincos,
271 f80 => __sincosx,
272 f128 => sincosq,
273 else => @compileError("unimplemented"),
274 };
275
276 var s: T = undefined;
277 var c: T = undefined;
278
279 f(0.0, &s, &c);
280 try expect(math.isPositiveZero(s));
281 try expect(c == 1.0);
282
283 f(-0.0, &s, &c);
284 try expect(math.isNegativeZero(s));
285 try expect(c == 1.0);
286
287 f(math.inf(T), &s, &c);
288 try expect(math.isNan(s));
289 try expect(math.isNan(c));
290
291 f(-math.inf(T), &s, &c);
292 try expect(math.isNan(s));
293 try expect(math.isNan(c));
294
295 f(math.nan(T), &s, &c);
296 try expect(math.isNan(s));
297 try expect(math.isNan(c));
298}
299
300test "sincos32.normal" {
301 const epsilon = math.floatEps(f32);
302 var s: f32 = undefined;
303 var c: f32 = undefined;
304
305 sincosf(0.0, &s, &c);
306 try expectApproxEqAbs(@as(f32, 0.0), s, epsilon);
307 try expectApproxEqAbs(@as(f32, 1.0), c, epsilon);
308
309 sincosf(0.2, &s, &c);
310 try expectApproxEqAbs(@as(f32, 0.19866933), s, epsilon);
311 try expectApproxEqAbs(@as(f32, 0.9800666), c, epsilon);
312
313 sincosf(0.8923, &s, &c);
314 try expectApproxEqAbs(@as(f32, 0.77851737), s, epsilon);
315 try expectApproxEqAbs(@as(f32, 0.6276231), c, epsilon);
316
317 sincosf(1.5, &s, &c);
318 try expectApproxEqAbs(@as(f32, 0.997495), s, epsilon);
319 try expectApproxEqAbs(@as(f32, 0.0707372), c, epsilon);
320
321 sincosf(-1.5, &s, &c);
322 try expectApproxEqAbs(@as(f32, -0.997495), s, epsilon);
323 try expectApproxEqAbs(@as(f32, 0.0707372), c, epsilon);
324
325 sincosf(37.45, &s, &c);
326 try expectApproxEqAbs(@as(f32, -0.24654257), s, epsilon);
327 try expectApproxEqAbs(@as(f32, 0.96913195), c, epsilon);
328
329 sincosf(89.123, &s, &c);
330 try expectApproxEqAbs(@as(f32, 0.9161657), s, epsilon);
331 try expectApproxEqAbs(@as(f32, 0.40079966), c, epsilon);
332}
333
334test "sincos32.special" {
335 try testSincosSpecial(f32);
336}
337
338test "sincos64.normal" {
339 const epsilon = math.floatEps(f64);
340 var s: f64 = undefined;
341 var c: f64 = undefined;
342
343 sincos(0.0, &s, &c);
344 try expectApproxEqAbs(@as(f64, 0.0), s, epsilon);
345 try expectApproxEqAbs(@as(f64, 1.0), c, epsilon);
346
347 sincos(0.2, &s, &c);
348 try expectApproxEqAbs(@as(f64, 0.19866933079506122), s, epsilon);
349 try expectApproxEqAbs(@as(f64, 0.9800665778412416), c, epsilon);
350
351 sincos(0.8923, &s, &c);
352 try expectApproxEqAbs(@as(f64, 0.7785173385577349), s, epsilon);
353 try expectApproxEqAbs(@as(f64, 0.6276230983360804), c, epsilon);
354
355 sincos(1.5, &s, &c);
356 try expectApproxEqAbs(@as(f64, 0.9974949866040544), s, epsilon);
357 try expectApproxEqAbs(@as(f64, 0.0707372016677029), c, epsilon);
358
359 sincos(-1.5, &s, &c);
360 try expectApproxEqAbs(@as(f64, -0.9974949866040544), s, epsilon);
361 try expectApproxEqAbs(@as(f64, 0.0707372016677029), c, epsilon);
362
363 sincos(37.45, &s, &c);
364 try expectApproxEqAbs(@as(f64, -0.24654331551411082), s, epsilon);
365 try expectApproxEqAbs(@as(f64, 0.9691317730707778), c, epsilon);
366
367 sincos(89.123, &s, &c);
368 try expectApproxEqAbs(@as(f64, 0.9161652766622714), s, epsilon);
369 try expectApproxEqAbs(@as(f64, 0.4008006809354791), c, epsilon);
370}
371
372test "sincos64.special" {
373 try testSincosSpecial(f64);
374}
375
376test "sincos80.normal" {
377 const epsilon = math.floatEps(f80);
378 var s: f80 = undefined;
379 var c: f80 = undefined;
380
381 __sincosx(0.0, &s, &c);
382 try expectApproxEqAbs(@as(f80, 0.0), s, epsilon);
383 try expectApproxEqAbs(@as(f80, 1.0), c, epsilon);
384
385 __sincosx(0.2, &s, &c);
386 try expectApproxEqAbs(@as(f80, 0.19866933079506121545941262711838975), s, epsilon);
387 try expectApproxEqAbs(@as(f80, 0.98006657784124163112419651674816888), c, epsilon);
388
389 __sincosx(0.8923, &s, &c);
390 try expectApproxEqAbs(@as(f80, 0.77851733855773487830689285621486050), s, epsilon);
391 try expectApproxEqAbs(@as(f80, 0.62762309833608037003563995939286067), c, epsilon);
392
393 __sincosx(1.5, &s, &c);
394 try expectApproxEqAbs(@as(f80, 0.99749498660405443094172337114148732), s, epsilon);
395 try expectApproxEqAbs(@as(f80, 0.070737201667702910088189851434268747), c, epsilon);
396
397 __sincosx(-1.5, &s, &c);
398 try expectApproxEqAbs(@as(f80, -0.99749498660405443094172337114148732), s, epsilon);
399 try expectApproxEqAbs(@as(f80, 0.070737201667702910088189851434268747), c, epsilon);
400
401 __sincosx(37.45, &s, &c);
402 try expectApproxEqAbs(@as(f80, -0.24654331551411356504), s, epsilon);
403 try expectApproxEqAbs(@as(f80, 0.9691317730707771246), c, epsilon);
404
405 __sincosx(89.123, &s, &c);
406 try expectApproxEqAbs(@as(f80, 0.91616527666226951006), s, epsilon);
407 try expectApproxEqAbs(@as(f80, 0.4008006809354834001), c, epsilon);
408}
409
410test "sincos80.special" {
411 try testSincosSpecial(f80);
412}
413
414test "sincos128.normal" {
415 const epsilon = math.floatEps(f128);
416 var s: f128 = undefined;
417 var c: f128 = undefined;
418
419 sincosq(0.0, &s, &c);
420 try expectApproxEqAbs(@as(f128, 0.0), s, epsilon);
421 try expectApproxEqAbs(@as(f128, 1.0), c, epsilon);
422
423 sincosq(0.2, &s, &c);
424 try expectApproxEqAbs(@as(f128, 0.19866933079506121545941262711838975), s, epsilon);
425 try expectApproxEqAbs(@as(f128, 0.98006657784124163112419651674816888), c, epsilon);
426
427 sincosq(0.8923, &s, &c);
428 try expectApproxEqAbs(@as(f128, 0.77851733855773487830689285621486050), s, epsilon);
429 try expectApproxEqAbs(@as(f128, 0.62762309833608037003563995939286067), c, epsilon);
430
431 sincosq(1.5, &s, &c);
432 try expectApproxEqAbs(@as(f128, 0.99749498660405443094172337114148732), s, epsilon);
433 try expectApproxEqAbs(@as(f128, 0.070737201667702910088189851434268747), c, epsilon);
434
435 sincosq(-1.5, &s, &c);
436 try expectApproxEqAbs(@as(f128, -0.99749498660405443094172337114148732), s, epsilon);
437 try expectApproxEqAbs(@as(f128, 0.070737201667702910088189851434268747), c, epsilon);
438
439 sincosq(37.45, &s, &c);
440 try expectApproxEqAbs(@as(f128, -0.24654331551411356571238581321661085), s, epsilon);
441 try expectApproxEqAbs(@as(f128, 0.96913177307077712443149563847233230), c, epsilon);
442
443 sincosq(89.123, &s, &c);
444 try expectApproxEqAbs(@as(f128, 0.91616527666226951075019849560482170), s, epsilon);
445 try expectApproxEqAbs(@as(f128, 0.40080068093548339848199454493704702), c, epsilon);
446}
447
448test "sincos128.special" {
449 try testSincosSpecial(f128);
450}
lib/libc/mingw/math/arm/sincos.S deleted-30
......@@ -1,30 +0,0 @@
1/**
2 * This file has no copyright assigned and is placed in the Public Domain.
3 * This file is part of the mingw-w64 runtime package.
4 * No warranty is given; refer to the file DISCLAIMER.PD within this package.
5 */
6#include <_mingw_mac.h>
7
8 .file "sincos.S"
9 .text
10 .align 2
11 /* zig patch: remove sincos symbol because sincos in compiler_rt is used instead */
12 .globl __MINGW_USYMBOL(sincosl)
13 .def __MINGW_USYMBOL(sincosl); .scl 2; .type 32; .endef
14__MINGW_USYMBOL(sincosl):
15 push {r4, r5, r11, lr}
16 add r11, sp, #8
17 vpush {d8}
18
19 mov r4, r0
20 mov r5, r1
21 vmov.f64 d8, d0
22 bl sin
23 vstr d0, [r4]
24
25 vmov.f64 d0, d8
26 bl cos
27 vstr d0, [r5]
28
29 vpop {d8}
30 pop {r4, r5, r11, pc}
lib/libc/mingw/math/arm64/sincos.S deleted-32
......@@ -1,32 +0,0 @@
1/**
2 * This file has no copyright assigned and is placed in the Public Domain.
3 * This file is part of the mingw-w64 runtime package.
4 * No warranty is given; refer to the file DISCLAIMER.PD within this package.
5 */
6#include <_mingw_mac.h>
7
8 .file "sincos.S"
9 .text
10 .align 2
11 /* zig patch: remove sincos symbol because sincos in compiler_rt is used instead */
12 .globl __MINGW_USYMBOL(sincosl)
13 .def __MINGW_USYMBOL(sincosl); .scl 2; .type 32; .endef
14__MINGW_USYMBOL(sincosl):
15 str d8, [sp, #-32]!
16 str x30, [sp, #8]
17 stp x19, x20, [sp, #16]
18
19 mov x19, x0
20 mov x20, x1
21 fmov d8, d0
22 bl sin
23 str d0, [x19]
24
25 fmov d0, d8
26 bl cos
27 str d0, [x20]
28
29 ldp x19, x20, [sp, #16]
30 ldr x30, [sp, #8]
31 ldr d8, [sp], #32
32 ret
src/libs/mingw.zig+1-5
......@@ -986,13 +986,9 @@ const mingw32_arm32_src = [_][]const u8{
986986 // mingwex
987987 "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "s_rint.c",
988988 "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "s_rintf.c",
989 "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "sincos.S",
990989};
991990
992const mingw32_arm64_src = [_][]const u8{
993 // mingwex
994 "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "sincos.S",
995};
991const mingw32_arm64_src = [_][]const u8{};
996992
997993const mingw32_winpthreads_src = [_][]const u8{
998994 // winpthreads