authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-10-11 05:51:47-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-10-11 05:51:47-04:00
logc3d67c5c4e2e86b472ae046f5e0e39db61009213
treeee9c747780c625a4fc18badecd868ef67cf7d51f
parentb316c25cc6f5b1703d7912da16c5c987f4406451
parenta06185f3620953b8402b26a12e80aee12cd9ac8d
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #13117 from topolarity/compiler-rt-cmul

compiler-rt: Implement complex multiply/division

23 files changed, 900 insertions(+), 197 deletions(-)

lib/compiler_rt.zig+15-1
......@@ -15,11 +15,25 @@ comptime {
1515 _ = @import("compiler_rt/subxf3.zig");
1616
1717 _ = @import("compiler_rt/mulf3.zig");
18 _ = @import("compiler_rt/muldf3.zig");
1918 _ = @import("compiler_rt/mulsf3.zig");
19 _ = @import("compiler_rt/muldf3.zig");
2020 _ = @import("compiler_rt/multf3.zig");
2121 _ = @import("compiler_rt/mulxf3.zig");
2222
23 _ = @import("compiler_rt/mulc3.zig");
24 _ = @import("compiler_rt/mulhc3.zig");
25 _ = @import("compiler_rt/mulsc3.zig");
26 _ = @import("compiler_rt/muldc3.zig");
27 _ = @import("compiler_rt/mulxc3.zig");
28 _ = @import("compiler_rt/multc3.zig");
29
30 _ = @import("compiler_rt/divc3.zig");
31 _ = @import("compiler_rt/divhc3.zig");
32 _ = @import("compiler_rt/divsc3.zig");
33 _ = @import("compiler_rt/divdc3.zig");
34 _ = @import("compiler_rt/divxc3.zig");
35 _ = @import("compiler_rt/divtc3.zig");
36
2337 _ = @import("compiler_rt/negsf2.zig");
2438 _ = @import("compiler_rt/negdf2.zig");
2539 _ = @import("compiler_rt/negtf2.zig");
lib/compiler_rt/divc3.zig created+62
......@@ -0,0 +1,62 @@
1const std = @import("std");
2const isNan = std.math.isNan;
3const isInf = std.math.isInf;
4const scalbn = std.math.scalbn;
5const ilogb = std.math.ilogb;
6const max = std.math.max;
7const fabs = std.math.fabs;
8const maxInt = std.math.maxInt;
9const minInt = std.math.minInt;
10const isFinite = std.math.isFinite;
11const copysign = std.math.copysign;
12const Complex = @import("mulc3.zig").Complex;
13
14/// Implementation based on Annex G of C17 Standard (N2176)
15pub inline fn divc3(comptime T: type, a: T, b: T, c_in: T, d_in: T) Complex(T) {
16 var c = c_in;
17 var d = d_in;
18
19 // logbw used to prevent under/over-flow
20 const logbw = ilogb(max(fabs(c), fabs(d)));
21 const logbw_finite = logbw != maxInt(i32) and logbw != minInt(i32);
22 const ilogbw = if (logbw_finite) b: {
23 c = scalbn(c, -logbw);
24 d = scalbn(d, -logbw);
25 break :b logbw;
26 } else 0;
27 const denom = c * c + d * d;
28 const result = Complex(T){
29 .real = scalbn((a * c + b * d) / denom, -ilogbw),
30 .imag = scalbn((b * c - a * d) / denom, -ilogbw),
31 };
32
33 // Recover infinities and zeros that computed as NaN+iNaN;
34 // the only cases are non-zero/zero, infinite/finite, and finite/infinite, ...
35 if (isNan(result.real) and isNan(result.imag)) {
36 const zero: T = 0.0;
37 const one: T = 1.0;
38
39 if ((denom == 0.0) and (!isNan(a) or !isNan(b))) {
40 return .{
41 .real = copysign(std.math.inf(T), c) * a,
42 .imag = copysign(std.math.inf(T), c) * b,
43 };
44 } else if ((isInf(a) or isInf(b)) and isFinite(c) and isFinite(d)) {
45 const boxed_a = copysign(if (isInf(a)) one else zero, a);
46 const boxed_b = copysign(if (isInf(b)) one else zero, b);
47 return .{
48 .real = std.math.inf(T) * (boxed_a * c - boxed_b * d),
49 .imag = std.math.inf(T) * (boxed_b * c - boxed_a * d),
50 };
51 } else if (logbw == maxInt(i32) and isFinite(a) and isFinite(b)) {
52 const boxed_c = copysign(if (isInf(c)) one else zero, c);
53 const boxed_d = copysign(if (isInf(d)) one else zero, d);
54 return .{
55 .real = 0.0 * (a * boxed_c + b * boxed_d),
56 .imag = 0.0 * (b * boxed_c - a * boxed_d),
57 };
58 }
59 }
60
61 return result;
62}
lib/compiler_rt/divc3_test.zig created+77
......@@ -0,0 +1,77 @@
1const std = @import("std");
2const math = std.math;
3const expect = std.testing.expect;
4
5const Complex = @import("./mulc3.zig").Complex;
6const __divhc3 = @import("./divhc3.zig").__divhc3;
7const __divsc3 = @import("./divsc3.zig").__divsc3;
8const __divdc3 = @import("./divdc3.zig").__divdc3;
9const __divxc3 = @import("./divxc3.zig").__divxc3;
10const __divtc3 = @import("./divtc3.zig").__divtc3;
11
12test {
13 try testDiv(f16, __divhc3);
14 try testDiv(f32, __divsc3);
15 try testDiv(f64, __divdc3);
16 try testDiv(f80, __divxc3);
17 try testDiv(f128, __divtc3);
18}
19
20fn testDiv(comptime T: type, comptime f: fn (T, T, T, T) callconv(.C) Complex(T)) !void {
21 {
22 var a: T = 1.0;
23 var b: T = 0.0;
24 var c: T = -1.0;
25 var d: T = 0.0;
26
27 const result = f(a, b, c, d);
28 try expect(result.real == -1.0);
29 try expect(result.imag == 0.0);
30 }
31 {
32 var a: T = 1.0;
33 var b: T = 0.0;
34 var c: T = -4.0;
35 var d: T = 0.0;
36
37 const result = f(a, b, c, d);
38 try expect(result.real == -0.25);
39 try expect(result.imag == 0.0);
40 }
41 {
42 // if the first operand is an infinity and the second operand is a finite number, then the
43 // result of the / operator is an infinity;
44 var a: T = -math.inf(T);
45 var b: T = 0.0;
46 var c: T = -4.0;
47 var d: T = 1.0;
48
49 const result = f(a, b, c, d);
50 try expect(result.real == math.inf(T));
51 try expect(result.imag == math.inf(T));
52 }
53 {
54 // if the first operand is a finite number and the second operand is an infinity, then the
55 // result of the / operator is a zero;
56 var a: T = 17.2;
57 var b: T = 0.0;
58 var c: T = -math.inf(T);
59 var d: T = 0.0;
60
61 const result = f(a, b, c, d);
62 try expect(result.real == -0.0);
63 try expect(result.imag == 0.0);
64 }
65 {
66 // if the first operand is a nonzero finite number or an infinity and the second operand is
67 // a zero, then the result of the / operator is an infinity
68 var a: T = 1.1;
69 var b: T = 0.1;
70 var c: T = 0.0;
71 var d: T = 0.0;
72
73 const result = f(a, b, c, d);
74 try expect(result.real == math.inf(T));
75 try expect(result.imag == math.inf(T));
76 }
77}
lib/compiler_rt/divdc3.zig created+11
......@@ -0,0 +1,11 @@
1const common = @import("./common.zig");
2const divc3 = @import("./divc3.zig");
3const Complex = @import("./mulc3.zig").Complex;
4
5comptime {
6 @export(__divdc3, .{ .name = "__divdc3", .linkage = common.linkage });
7}
8
9pub fn __divdc3(a: f64, b: f64, c: f64, d: f64) callconv(.C) Complex(f64) {
10 return divc3.divc3(f64, a, b, c, d);
11}
lib/compiler_rt/divhc3.zig created+11
......@@ -0,0 +1,11 @@
1const common = @import("./common.zig");
2const divc3 = @import("./divc3.zig");
3const Complex = @import("./mulc3.zig").Complex;
4
5comptime {
6 @export(__divhc3, .{ .name = "__divhc3", .linkage = common.linkage });
7}
8
9pub fn __divhc3(a: f16, b: f16, c: f16, d: f16) callconv(.C) Complex(f16) {
10 return divc3.divc3(f16, a, b, c, d);
11}
lib/compiler_rt/divsc3.zig created+11
......@@ -0,0 +1,11 @@
1const common = @import("./common.zig");
2const divc3 = @import("./divc3.zig");
3const Complex = @import("./mulc3.zig").Complex;
4
5comptime {
6 @export(__divsc3, .{ .name = "__divsc3", .linkage = common.linkage });
7}
8
9pub fn __divsc3(a: f32, b: f32, c: f32, d: f32) callconv(.C) Complex(f32) {
10 return divc3.divc3(f32, a, b, c, d);
11}
lib/compiler_rt/divtc3.zig created+11
......@@ -0,0 +1,11 @@
1const common = @import("./common.zig");
2const divc3 = @import("./divc3.zig");
3const Complex = @import("./mulc3.zig").Complex;
4
5comptime {
6 @export(__divtc3, .{ .name = "__divtc3", .linkage = common.linkage });
7}
8
9pub fn __divtc3(a: f128, b: f128, c: f128, d: f128) callconv(.C) Complex(f128) {
10 return divc3.divc3(f128, a, b, c, d);
11}
lib/compiler_rt/divxc3.zig created+11
......@@ -0,0 +1,11 @@
1const common = @import("./common.zig");
2const divc3 = @import("./divc3.zig");
3const Complex = @import("./mulc3.zig").Complex;
4
5comptime {
6 @export(__divxc3, .{ .name = "__divxc3", .linkage = common.linkage });
7}
8
9pub fn __divxc3(a: f80, b: f80, c: f80, d: f80) callconv(.C) Complex(f80) {
10 return divc3.divc3(f80, a, b, c, d);
11}
lib/compiler_rt/extenddfxf2.zig+1-1
......@@ -7,6 +7,6 @@ comptime {
77 @export(__extenddfxf2, .{ .name = "__extenddfxf2", .linkage = common.linkage });
88}
99
10fn __extenddfxf2(a: f64) callconv(.C) f80 {
10pub fn __extenddfxf2(a: f64) callconv(.C) f80 {
1111 return extend_f80(f64, @bitCast(u64, a));
1212}
lib/compiler_rt/extendf.zig+3-1
......@@ -92,6 +92,8 @@ pub inline fn extend_f80(comptime src_t: type, a: std.meta.Int(.unsigned, @typeI
9292 const src_qnan = 1 << (src_sig_bits - 1);
9393 const src_nan_code = src_qnan - 1;
9494
95 const SrcShift = std.math.Log2Int(src_rep_t);
96
9597 var dst: std.math.F80 = undefined;
9698
9799 // Break a into a sign and representation of the absolute value
......@@ -124,7 +126,7 @@ pub inline fn extend_f80(comptime src_t: type, a: std.meta.Int(.unsigned, @typeI
124126
125127 dst.fraction = @as(u64, a_abs) << @intCast(u6, dst_sig_bits - src_sig_bits + scale);
126128 dst.fraction |= dst_int_bit; // bit 64 is always set for normal numbers
127 dst.exp = @truncate(u16, a_abs >> @intCast(u4, src_sig_bits - scale));
129 dst.exp = @truncate(u16, a_abs >> @intCast(SrcShift, src_sig_bits - scale));
128130 dst.exp ^= 1;
129131 dst.exp |= dst_exp_bias - src_exp_bias - scale + 1;
130132 } else {
lib/compiler_rt/extendf_test.zig+48
......@@ -1,10 +1,27 @@
1const std = @import("std");
2const math = std.math;
13const builtin = @import("builtin");
24const __extendhfsf2 = @import("extendhfsf2.zig").__extendhfsf2;
35const __extendhftf2 = @import("extendhftf2.zig").__extendhftf2;
46const __extendsftf2 = @import("extendsftf2.zig").__extendsftf2;
57const __extenddftf2 = @import("extenddftf2.zig").__extenddftf2;
8const __extenddfxf2 = @import("extenddfxf2.zig").__extenddfxf2;
69const F16T = @import("./common.zig").F16T;
710
11fn test__extenddfxf2(a: f64, expected: u80) !void {
12 const x = __extenddfxf2(a);
13
14 const rep = @bitCast(u80, x);
15 if (rep == expected)
16 return;
17
18 // test other possible NaN representation(signal NaN)
19 if (math.isNan(@bitCast(f80, expected)) and math.isNan(x))
20 return;
21
22 @panic("__extenddfxf2 test failure");
23}
24
825fn test__extenddftf2(a: f64, expected_hi: u64, expected_lo: u64) !void {
926 const x = __extenddftf2(a);
1027
......@@ -65,6 +82,33 @@ fn test__extendsftf2(a: f32, expected_hi: u64, expected_lo: u64) !void {
6582 return error.TestFailure;
6683}
6784
85test "extenddfxf2" {
86 // qNaN
87 try test__extenddfxf2(makeQNaN64(), 0x7fffc000000000000000);
88
89 // NaN
90 try test__extenddfxf2(makeNaN64(0x7100000000000), 0x7fffe080000000000000);
91 // This is bad?
92
93 // inf
94 try test__extenddfxf2(makeInf64(), 0x7fff8000000000000000);
95
96 // zero
97 try test__extenddfxf2(0.0, 0x0);
98
99 try test__extenddfxf2(0x0.a3456789abcdefp+6, 0x4004a3456789abcdf000);
100
101 try test__extenddfxf2(0x0.edcba987654321fp-8, 0x3ff6edcba98765432000);
102
103 try test__extenddfxf2(0x0.a3456789abcdefp+46, 0x402ca3456789abcdf000);
104
105 try test__extenddfxf2(0x0.edcba987654321fp-44, 0x3fd2edcba98765432000);
106
107 // subnormal
108 try test__extenddfxf2(0x1.8000000000001p-1022, 0x3c01c000000000000800);
109 try test__extenddfxf2(0x1.8000000000002p-1023, 0x3c00c000000000001000);
110}
111
68112test "extenddftf2" {
69113 // qNaN
70114 try test__extenddftf2(makeQNaN64(), 0x7fff800000000000, 0x0);
......@@ -85,6 +129,10 @@ test "extenddftf2" {
85129 try test__extenddftf2(0x1.23456789abcdefp+45, 0x402c23456789abcd, 0xf000000000000000);
86130
87131 try test__extenddftf2(0x1.edcba987654321fp-45, 0x3fd2edcba9876543, 0x2000000000000000);
132
133 // subnormal
134 try test__extenddftf2(0x1.8p-1022, 0x3c01800000000000, 0x0);
135 try test__extenddftf2(0x1.8p-1023, 0x3c00800000000000, 0x0);
88136}
89137
90138test "extendhfsf2" {
lib/compiler_rt/mulc3.zig created+79
......@@ -0,0 +1,79 @@
1const std = @import("std");
2const isNan = std.math.isNan;
3const isInf = std.math.isInf;
4const copysign = std.math.copysign;
5
6pub fn Complex(comptime T: type) type {
7 return extern struct {
8 real: T,
9 imag: T,
10 };
11}
12
13/// Implementation based on Annex G of C17 Standard (N2176)
14pub inline fn mulc3(comptime T: type, a_in: T, b_in: T, c_in: T, d_in: T) Complex(T) {
15 var a = a_in;
16 var b = b_in;
17 var c = c_in;
18 var d = d_in;
19
20 const ac = a * c;
21 const bd = b * d;
22 const ad = a * d;
23 const bc = b * c;
24
25 const zero: T = 0.0;
26 const one: T = 1.0;
27
28 var z = Complex(T){
29 .real = ac - bd,
30 .imag = ad + bc,
31 };
32 if (isNan(z.real) and isNan(z.imag)) {
33 var recalc: bool = false;
34
35 if (isInf(a) or isInf(b)) { // (a + ib) is infinite
36
37 // "Box" the infinity (+/-inf goes to +/-1, all finite values go to 0)
38 a = copysign(if (isInf(a)) one else zero, a);
39 b = copysign(if (isInf(b)) one else zero, b);
40
41 // Replace NaNs in the other factor with (signed) 0
42 if (isNan(c)) c = copysign(zero, c);
43 if (isNan(d)) d = copysign(zero, d);
44
45 recalc = true;
46 }
47
48 if (isInf(c) or isInf(d)) { // (c + id) is infinite
49
50 // "Box" the infinity (+/-inf goes to +/-1, all finite values go to 0)
51 c = copysign(if (isInf(c)) one else zero, c);
52 d = copysign(if (isInf(d)) one else zero, d);
53
54 // Replace NaNs in the other factor with (signed) 0
55 if (isNan(a)) a = copysign(zero, a);
56 if (isNan(b)) b = copysign(zero, b);
57
58 recalc = true;
59 }
60
61 if (!recalc and (isInf(ac) or isInf(bd) or isInf(ad) or isInf(bc))) {
62
63 // Recover infinities from overflow by changing NaNs to 0
64 if (isNan(a)) a = copysign(zero, a);
65 if (isNan(b)) b = copysign(zero, b);
66 if (isNan(c)) c = copysign(zero, c);
67 if (isNan(d)) d = copysign(zero, d);
68
69 recalc = true;
70 }
71 if (recalc) {
72 return .{
73 .real = std.math.inf(T) * (a * c - b * d),
74 .imag = std.math.inf(T) * (a * d + b * c),
75 };
76 }
77 }
78 return z;
79}
lib/compiler_rt/mulc3_test.zig created+65
......@@ -0,0 +1,65 @@
1const std = @import("std");
2const math = std.math;
3const expect = std.testing.expect;
4
5const Complex = @import("./mulc3.zig").Complex;
6const __mulhc3 = @import("./mulhc3.zig").__mulhc3;
7const __mulsc3 = @import("./mulsc3.zig").__mulsc3;
8const __muldc3 = @import("./muldc3.zig").__muldc3;
9const __mulxc3 = @import("./mulxc3.zig").__mulxc3;
10const __multc3 = @import("./multc3.zig").__multc3;
11
12test {
13 try testMul(f16, __mulhc3);
14 try testMul(f32, __mulsc3);
15 try testMul(f64, __muldc3);
16 try testMul(f80, __mulxc3);
17 try testMul(f128, __multc3);
18}
19
20fn testMul(comptime T: type, comptime f: fn (T, T, T, T) callconv(.C) Complex(T)) !void {
21 {
22 var a: T = 1.0;
23 var b: T = 0.0;
24 var c: T = -1.0;
25 var d: T = 0.0;
26
27 const result = f(a, b, c, d);
28 try expect(result.real == -1.0);
29 try expect(result.imag == 0.0);
30 }
31 {
32 var a: T = 1.0;
33 var b: T = 0.0;
34 var c: T = -4.0;
35 var d: T = 0.0;
36
37 const result = f(a, b, c, d);
38 try expect(result.real == -4.0);
39 try expect(result.imag == 0.0);
40 }
41 {
42 // if one operand is an infinity and the other operand is a nonzero finite number or an infinity,
43 // then the result of the * operator is an infinity;
44 var a: T = math.inf(T);
45 var b: T = -math.inf(T);
46 var c: T = 1.0;
47 var d: T = 0.0;
48
49 const result = f(a, b, c, d);
50 try expect(result.real == math.inf(T));
51 try expect(result.imag == -math.inf(T));
52 }
53 {
54 // if one operand is an infinity and the other operand is a nonzero finite number or an infinity,
55 // then the result of the * operator is an infinity;
56 var a: T = math.inf(T);
57 var b: T = -1.0;
58 var c: T = 1.0;
59 var d: T = math.inf(T);
60
61 const result = f(a, b, c, d);
62 try expect(result.real == math.inf(T));
63 try expect(result.imag == math.inf(T));
64 }
65}
lib/compiler_rt/muldc3.zig created+12
......@@ -0,0 +1,12 @@
1const common = @import("./common.zig");
2const mulc3 = @import("./mulc3.zig");
3
4pub const panic = common.panic;
5
6comptime {
7 @export(__muldc3, .{ .name = "__muldc3", .linkage = common.linkage });
8}
9
10pub fn __muldc3(a: f64, b: f64, c: f64, d: f64) callconv(.C) mulc3.Complex(f64) {
11 return mulc3.mulc3(f64, a, b, c, d);
12}
lib/compiler_rt/mulhc3.zig created+12
......@@ -0,0 +1,12 @@
1const common = @import("./common.zig");
2const mulc3 = @import("./mulc3.zig");
3
4pub const panic = common.panic;
5
6comptime {
7 @export(__mulhc3, .{ .name = "__mulhc3", .linkage = common.linkage });
8}
9
10pub fn __mulhc3(a: f16, b: f16, c: f16, d: f16) callconv(.C) mulc3.Complex(f16) {
11 return mulc3.mulc3(f16, a, b, c, d);
12}
lib/compiler_rt/mulsc3.zig created+12
......@@ -0,0 +1,12 @@
1const common = @import("./common.zig");
2const mulc3 = @import("./mulc3.zig");
3
4pub const panic = common.panic;
5
6comptime {
7 @export(__mulsc3, .{ .name = "__mulsc3", .linkage = common.linkage });
8}
9
10pub fn __mulsc3(a: f32, b: f32, c: f32, d: f32) callconv(.C) mulc3.Complex(f32) {
11 return mulc3.mulc3(f32, a, b, c, d);
12}
lib/compiler_rt/multc3.zig created+12
......@@ -0,0 +1,12 @@
1const common = @import("./common.zig");
2const mulc3 = @import("./mulc3.zig");
3
4pub const panic = common.panic;
5
6comptime {
7 @export(__multc3, .{ .name = "__multc3", .linkage = common.linkage });
8}
9
10pub fn __multc3(a: f128, b: f128, c: f128, d: f128) callconv(.C) mulc3.Complex(f128) {
11 return mulc3.mulc3(f128, a, b, c, d);
12}
lib/compiler_rt/mulxc3.zig created+12
......@@ -0,0 +1,12 @@
1const common = @import("./common.zig");
2const mulc3 = @import("./mulc3.zig");
3
4pub const panic = common.panic;
5
6comptime {
7 @export(__mulxc3, .{ .name = "__mulxc3", .linkage = common.linkage });
8}
9
10pub fn __mulxc3(a: f80, b: f80, c: f80, d: f80) callconv(.C) mulc3.Complex(f80) {
11 return mulc3.mulc3(f80, a, b, c, d);
12}
lib/std/math/ilogb.zig+110-128
......@@ -15,175 +15,157 @@ const minInt = std.math.minInt;
1515///
1616/// Special Cases:
1717/// - ilogb(+-inf) = maxInt(i32)
18/// - ilogb(0) = maxInt(i32)
19/// - ilogb(nan) = maxInt(i32)
18/// - ilogb(+-0) = minInt(i32)
19/// - ilogb(nan) = minInt(i32)
2020pub fn ilogb(x: anytype) i32 {
2121 const T = @TypeOf(x);
22 return switch (T) {
23 f32 => ilogb32(x),
24 f64 => ilogb64(x),
25 f128 => ilogb128(x),
26 else => @compileError("ilogb not implemented for " ++ @typeName(T)),
27 };
22 return ilogbX(T, x);
2823}
2924
30// TODO: unify these implementations with generics
25pub const fp_ilogbnan = minInt(i32);
26pub const fp_ilogb0 = minInt(i32);
3127
32// NOTE: Should these be exposed publicly?
33const fp_ilogbnan = -1 - @as(i32, maxInt(u32) >> 1);
34const fp_ilogb0 = fp_ilogbnan;
28fn ilogbX(comptime T: type, x: T) i32 {
29 const typeWidth = @typeInfo(T).Float.bits;
30 const significandBits = math.floatMantissaBits(T);
31 const exponentBits = math.floatExponentBits(T);
3532
36fn ilogb32(x: f32) i32 {
37 var u = @bitCast(u32, x);
38 var e = @intCast(i32, (u >> 23) & 0xFF);
33 const Z = std.meta.Int(.unsigned, typeWidth);
3934
40 // TODO: We should be able to merge this with the lower check.
41 if (math.isNan(x)) {
42 return maxInt(i32);
43 }
35 const signBit = (@as(Z, 1) << (significandBits + exponentBits));
36 const maxExponent = ((1 << exponentBits) - 1);
37 const exponentBias = (maxExponent >> 1);
4438
45 if (e == 0) {
46 u <<= 9;
47 if (u == 0) {
48 math.raiseInvalid();
49 return fp_ilogb0;
50 }
39 const absMask = signBit - 1;
5140
52 // subnormal
53 e = -0x7F;
54 while (u >> 31 == 0) : (u <<= 1) {
55 e -= 1;
56 }
57 return e;
58 }
59
60 if (e == 0xFF) {
61 math.raiseInvalid();
62 if (u << 9 != 0) {
63 return fp_ilogbnan;
64 } else {
65 return maxInt(i32);
66 }
67 }
68
69 return e - 0x7F;
70}
71
72fn ilogb64(x: f64) i32 {
73 var u = @bitCast(u64, x);
74 var e = @intCast(i32, (u >> 52) & 0x7FF);
75
76 if (math.isNan(x)) {
77 return maxInt(i32);
78 }
41 var u = @bitCast(Z, x) & absMask;
42 var e = @intCast(i32, u >> significandBits);
7943
8044 if (e == 0) {
81 u <<= 12;
8245 if (u == 0) {
8346 math.raiseInvalid();
8447 return fp_ilogb0;
8548 }
8649
87 // subnormal
88 e = -0x3FF;
89 while (u >> 63 == 0) : (u <<= 1) {
90 e -= 1;
91 }
92 return e;
50 // offset sign bit, exponent bits, and integer bit (if present) + bias
51 const offset = 1 + exponentBits + @boolToInt(T == f80) - exponentBias;
52 return offset - @intCast(i32, @clz(u));
9353 }
9454
95 if (e == 0x7FF) {
55 if (e == maxExponent) {
9656 math.raiseInvalid();
97 if (u << 12 != 0) {
98 return fp_ilogbnan;
99 } else {
100 return maxInt(i32);
101 }
57 if (u > @bitCast(Z, math.inf(T))) {
58 return fp_ilogbnan; // u is a NaN
59 } else return maxInt(i32);
10260 }
10361
104 return e - 0x3FF;
62 return e - exponentBias;
10563}
10664
107fn ilogb128(x: f128) i32 {
108 var u = @bitCast(u128, x);
109 var e = @intCast(i32, (u >> 112) & 0x7FFF);
110
111 if (math.isNan(x)) {
112 return maxInt(i32);
113 }
114
115 if (e == 0) {
116 u <<= 16;
117 if (u == 0) {
118 math.raiseInvalid();
119 return fp_ilogb0;
120 }
121
122 // subnormal x
123 return ilogb128(x * 0x1p120) - 120;
124 }
125
126 if (e == 0x7FFF) {
127 math.raiseInvalid();
128 if (u << 16 != 0) {
129 return fp_ilogbnan;
130 } else {
131 return maxInt(i32);
132 }
133 }
134
135 return e - 0x3FFF;
65test "type dispatch" {
66 try expect(ilogb(@as(f32, 0.2)) == ilogbX(f32, 0.2));
67 try expect(ilogb(@as(f64, 0.2)) == ilogbX(f64, 0.2));
13668}
13769
138test "type dispatch" {
139 try expect(ilogb(@as(f32, 0.2)) == ilogb32(0.2));
140 try expect(ilogb(@as(f64, 0.2)) == ilogb64(0.2));
70test "16" {
71 try expect(ilogbX(f16, 0.0) == fp_ilogb0);
72 try expect(ilogbX(f16, 0.5) == -1);
73 try expect(ilogbX(f16, 0.8923) == -1);
74 try expect(ilogbX(f16, 10.0) == 3);
75 try expect(ilogbX(f16, -65504) == 15);
76 try expect(ilogbX(f16, 2398.23) == 11);
77
78 try expect(ilogbX(f16, 0x1p-1) == -1);
79 try expect(ilogbX(f16, 0x1p-17) == -17);
80 try expect(ilogbX(f16, 0x1p-24) == -24);
14181}
14282
14383test "32" {
144 try expect(ilogb32(0.0) == fp_ilogb0);
145 try expect(ilogb32(0.5) == -1);
146 try expect(ilogb32(0.8923) == -1);
147 try expect(ilogb32(10.0) == 3);
148 try expect(ilogb32(-123984) == 16);
149 try expect(ilogb32(2398.23) == 11);
84 try expect(ilogbX(f32, 0.0) == fp_ilogb0);
85 try expect(ilogbX(f32, 0.5) == -1);
86 try expect(ilogbX(f32, 0.8923) == -1);
87 try expect(ilogbX(f32, 10.0) == 3);
88 try expect(ilogbX(f32, -123984) == 16);
89 try expect(ilogbX(f32, 2398.23) == 11);
90
91 try expect(ilogbX(f32, 0x1p-1) == -1);
92 try expect(ilogbX(f32, 0x1p-122) == -122);
93 try expect(ilogbX(f32, 0x1p-127) == -127);
15094}
15195
15296test "64" {
153 try expect(ilogb64(0.0) == fp_ilogb0);
154 try expect(ilogb64(0.5) == -1);
155 try expect(ilogb64(0.8923) == -1);
156 try expect(ilogb64(10.0) == 3);
157 try expect(ilogb64(-123984) == 16);
158 try expect(ilogb64(2398.23) == 11);
97 try expect(ilogbX(f64, 0.0) == fp_ilogb0);
98 try expect(ilogbX(f64, 0.5) == -1);
99 try expect(ilogbX(f64, 0.8923) == -1);
100 try expect(ilogbX(f64, 10.0) == 3);
101 try expect(ilogbX(f64, -123984) == 16);
102 try expect(ilogbX(f64, 2398.23) == 11);
103
104 try expect(ilogbX(f64, 0x1p-1) == -1);
105 try expect(ilogbX(f64, 0x1p-127) == -127);
106 try expect(ilogbX(f64, 0x1p-1012) == -1012);
107 try expect(ilogbX(f64, 0x1p-1023) == -1023);
108}
109
110test "80" {
111 try expect(ilogbX(f80, 0.0) == fp_ilogb0);
112 try expect(ilogbX(f80, 0.5) == -1);
113 try expect(ilogbX(f80, 0.8923) == -1);
114 try expect(ilogbX(f80, 10.0) == 3);
115 try expect(ilogbX(f80, -123984) == 16);
116 try expect(ilogbX(f80, 2398.23) == 11);
117
118 try expect(ilogbX(f80, 0x1p-1) == -1);
119 try expect(ilogbX(f80, 0x1p-127) == -127);
120 try expect(ilogbX(f80, 0x1p-1023) == -1023);
121 try expect(ilogbX(f80, 0x1p-16383) == -16383);
159122}
160123
161124test "128" {
162 try expect(ilogb128(0.0) == fp_ilogb0);
163 try expect(ilogb128(0.5) == -1);
164 try expect(ilogb128(0.8923) == -1);
165 try expect(ilogb128(10.0) == 3);
166 try expect(ilogb128(-123984) == 16);
167 try expect(ilogb128(2398.23) == 11);
125 try expect(ilogbX(f128, 0.0) == fp_ilogb0);
126 try expect(ilogbX(f128, 0.5) == -1);
127 try expect(ilogbX(f128, 0.8923) == -1);
128 try expect(ilogbX(f128, 10.0) == 3);
129 try expect(ilogbX(f128, -123984) == 16);
130 try expect(ilogbX(f128, 2398.23) == 11);
131
132 try expect(ilogbX(f128, 0x1p-1) == -1);
133 try expect(ilogbX(f128, 0x1p-127) == -127);
134 try expect(ilogbX(f128, 0x1p-1023) == -1023);
135 try expect(ilogbX(f128, 0x1p-16383) == -16383);
136}
137
138test "16 special" {
139 try expect(ilogbX(f16, math.inf(f16)) == maxInt(i32));
140 try expect(ilogbX(f16, -math.inf(f16)) == maxInt(i32));
141 try expect(ilogbX(f16, 0.0) == minInt(i32));
142 try expect(ilogbX(f16, math.nan(f16)) == fp_ilogbnan);
168143}
169144
170145test "32 special" {
171 try expect(ilogb32(math.inf(f32)) == maxInt(i32));
172 try expect(ilogb32(-math.inf(f32)) == maxInt(i32));
173 try expect(ilogb32(0.0) == minInt(i32));
174 try expect(ilogb32(math.nan(f32)) == maxInt(i32));
146 try expect(ilogbX(f32, math.inf(f32)) == maxInt(i32));
147 try expect(ilogbX(f32, -math.inf(f32)) == maxInt(i32));
148 try expect(ilogbX(f32, 0.0) == minInt(i32));
149 try expect(ilogbX(f32, math.nan(f32)) == fp_ilogbnan);
175150}
176151
177152test "64 special" {
178 try expect(ilogb64(math.inf(f64)) == maxInt(i32));
179 try expect(ilogb64(-math.inf(f64)) == maxInt(i32));
180 try expect(ilogb64(0.0) == minInt(i32));
181 try expect(ilogb64(math.nan(f64)) == maxInt(i32));
153 try expect(ilogbX(f64, math.inf(f64)) == maxInt(i32));
154 try expect(ilogbX(f64, -math.inf(f64)) == maxInt(i32));
155 try expect(ilogbX(f64, 0.0) == minInt(i32));
156 try expect(ilogbX(f64, math.nan(f64)) == fp_ilogbnan);
157}
158
159test "80 special" {
160 try expect(ilogbX(f80, math.inf(f80)) == maxInt(i32));
161 try expect(ilogbX(f80, -math.inf(f80)) == maxInt(i32));
162 try expect(ilogbX(f80, 0.0) == minInt(i32));
163 try expect(ilogbX(f80, math.nan(f80)) == fp_ilogbnan);
182164}
183165
184166test "128 special" {
185 try expect(ilogb128(math.inf(f128)) == maxInt(i32));
186 try expect(ilogb128(-math.inf(f128)) == maxInt(i32));
187 try expect(ilogb128(0.0) == minInt(i32));
188 try expect(ilogb128(math.nan(f128)) == maxInt(i32));
167 try expect(ilogbX(f128, math.inf(f128)) == maxInt(i32));
168 try expect(ilogbX(f128, -math.inf(f128)) == maxInt(i32));
169 try expect(ilogbX(f128, 0.0) == minInt(i32));
170 try expect(ilogbX(f128, math.nan(f128)) == fp_ilogbnan);
189171}
lib/std/math/ldexp.zig+123-66
......@@ -1,91 +1,148 @@
1// Ported from musl, which is licensed under the MIT license:
2// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
3//
4// https://git.musl-libc.org/cgit/musl/tree/src/math/ldexpf.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/ldexp.c
6
71const std = @import("std");
82const math = std.math;
3const Log2Int = std.math.Log2Int;
94const assert = std.debug.assert;
105const expect = std.testing.expect;
116
127/// Returns x * 2^n.
138pub fn ldexp(x: anytype, n: i32) @TypeOf(x) {
14 var base = x;
15 var shift = n;
16
17 const T = @TypeOf(base);
9 const T = @TypeOf(x);
1810 const TBits = std.meta.Int(.unsigned, @typeInfo(T).Float.bits);
1911
12 const exponent_bits = math.floatExponentBits(T);
2013 const mantissa_bits = math.floatMantissaBits(T);
21 const exponent_min = math.floatExponentMin(T);
22 const exponent_max = math.floatExponentMax(T);
23
24 const exponent_bias = exponent_max;
25
26 // fix double rounding errors in subnormal ranges
27 // https://git.musl-libc.org/cgit/musl/commit/src/math/ldexp.c?id=8c44a060243f04283ca68dad199aab90336141db
28 const scale_min_expo = exponent_min + mantissa_bits + 1;
29 const scale_min = @bitCast(T, @as(TBits, scale_min_expo + exponent_bias) << mantissa_bits);
30 const scale_max = @bitCast(T, @intCast(TBits, exponent_max + exponent_bias) << mantissa_bits);
31
32 // scale `shift` within floating point limits, if possible
33 // second pass is possible due to subnormal range
34 // third pass always results in +/-0.0 or +/-inf
35 if (shift > exponent_max) {
36 base *= scale_max;
37 shift -= exponent_max;
38 if (shift > exponent_max) {
39 base *= scale_max;
40 shift -= exponent_max;
41 if (shift > exponent_max) shift = exponent_max;
14 const fractional_bits = math.floatFractionalBits(T);
15
16 const max_biased_exponent = 2 * math.floatExponentMax(T);
17 const mantissa_mask = @as(TBits, (1 << mantissa_bits) - 1);
18
19 const repr = @bitCast(TBits, x);
20 const sign_bit = repr & (1 << (exponent_bits + mantissa_bits));
21
22 if (math.isNan(x) or !math.isFinite(x))
23 return x;
24
25 var exponent: i32 = @intCast(i32, (repr << 1) >> (mantissa_bits + 1));
26 if (exponent == 0)
27 exponent += (@as(i32, exponent_bits) + @boolToInt(T == f80)) - @clz(repr << 1);
28
29 if (n >= 0) {
30 if (n > max_biased_exponent - exponent) {
31 // Overflow. Return +/- inf
32 return @bitCast(T, @bitCast(TBits, math.inf(T)) | sign_bit);
33 } else if (exponent + n <= 0) {
34 // Result is subnormal
35 return @bitCast(T, (repr << @intCast(Log2Int(TBits), n)) | sign_bit);
36 } else if (exponent <= 0) {
37 // Result is normal, but needs shifting
38 var result = @intCast(TBits, n + exponent) << mantissa_bits;
39 result |= (repr << @intCast(Log2Int(TBits), 1 - exponent)) & mantissa_mask;
40 return @bitCast(T, result | sign_bit);
4241 }
43 } else if (shift < exponent_min) {
44 base *= scale_min;
45 shift -= scale_min_expo;
46 if (shift < exponent_min) {
47 base *= scale_min;
48 shift -= scale_min_expo;
49 if (shift < exponent_min) shift = exponent_min;
42
43 // Result needs no shifting
44 return @bitCast(T, repr + (@intCast(TBits, n) << mantissa_bits));
45 } else {
46 if (n <= -exponent) {
47 if (n < -(mantissa_bits + exponent))
48 return @bitCast(T, sign_bit); // Severe underflow. Return +/- 0
49
50 // Result underflowed, we need to shift and round
51 const shift = @intCast(Log2Int(TBits), math.min(-n, -(exponent + n) + 1));
52 const exact_tie: bool = @ctz(repr) == shift - 1;
53 var result = repr & mantissa_mask;
54
55 if (T != f80) // Include integer bit
56 result |= @as(TBits, @boolToInt(exponent > 0)) << fractional_bits;
57 result = @intCast(TBits, (result >> (shift - 1)));
58
59 // Round result, including round-to-even for exact ties
60 result = ((result + 1) >> 1) & ~@as(TBits, @boolToInt(exact_tie));
61 return @bitCast(T, result | sign_bit);
5062 }
51 }
5263
53 return base * @bitCast(T, @intCast(TBits, shift + exponent_bias) << mantissa_bits);
64 // Result is exact, and needs no shifting
65 return @bitCast(T, repr - (@intCast(TBits, -n) << mantissa_bits));
66 }
5467}
5568
5669test "math.ldexp" {
57 // TODO derive the various constants here with new maths API
58
59 // basic usage
60 try expect(ldexp(@as(f16, 1.5), 4) == 24.0);
61 try expect(ldexp(@as(f32, 1.5), 4) == 24.0);
62 try expect(ldexp(@as(f64, 1.5), 4) == 24.0);
63 try expect(ldexp(@as(f128, 1.5), 4) == 24.0);
6470
6571 // subnormals
66 try expect(math.isNormal(ldexp(@as(f16, 1.0), -14)));
67 try expect(!math.isNormal(ldexp(@as(f16, 1.0), -15)));
68 try expect(math.isNormal(ldexp(@as(f32, 1.0), -126)));
69 try expect(!math.isNormal(ldexp(@as(f32, 1.0), -127)));
70 try expect(math.isNormal(ldexp(@as(f64, 1.0), -1022)));
71 try expect(!math.isNormal(ldexp(@as(f64, 1.0), -1023)));
72 try expect(math.isNormal(ldexp(@as(f128, 1.0), -16382)));
73 try expect(!math.isNormal(ldexp(@as(f128, 1.0), -16383)));
74 // unreliable due to lack of native f16 support, see talk on PR #8733
75 // try expect(ldexp(@as(f16, 0x1.1FFp-1), -14 - 9) == math.floatTrueMin(f16));
72 try expect(ldexp(@as(f16, 0x1.1FFp14), -14 - 9 - 15) == math.floatTrueMin(f16));
7673 try expect(ldexp(@as(f32, 0x1.3FFFFFp-1), -126 - 22) == math.floatTrueMin(f32));
7774 try expect(ldexp(@as(f64, 0x1.7FFFFFFFFFFFFp-1), -1022 - 51) == math.floatTrueMin(f64));
75 try expect(ldexp(@as(f80, 0x1.7FFFFFFFFFFFFFFEp-1), -16382 - 62) == math.floatTrueMin(f80));
7876 try expect(ldexp(@as(f128, 0x1.7FFFFFFFFFFFFFFFFFFFFFFFFFFFp-1), -16382 - 111) == math.floatTrueMin(f128));
7977
80 // float limits
8178 try expect(ldexp(math.floatMax(f32), -128 - 149) > 0.0);
8279 try expect(ldexp(math.floatMax(f32), -128 - 149 - 1) == 0.0);
83 try expect(!math.isPositiveInf(ldexp(math.floatTrueMin(f16), 15 + 24)));
84 try expect(math.isPositiveInf(ldexp(math.floatTrueMin(f16), 15 + 24 + 1)));
85 try expect(!math.isPositiveInf(ldexp(math.floatTrueMin(f32), 127 + 149)));
86 try expect(math.isPositiveInf(ldexp(math.floatTrueMin(f32), 127 + 149 + 1)));
87 try expect(!math.isPositiveInf(ldexp(math.floatTrueMin(f64), 1023 + 1074)));
88 try expect(math.isPositiveInf(ldexp(math.floatTrueMin(f64), 1023 + 1074 + 1)));
89 try expect(!math.isPositiveInf(ldexp(math.floatTrueMin(f128), 16383 + 16494)));
90 try expect(math.isPositiveInf(ldexp(math.floatTrueMin(f128), 16383 + 16494 + 1)));
80
81 @setEvalBranchQuota(10_000);
82
83 inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| {
84 const fractional_bits = math.floatFractionalBits(T);
85
86 const min_exponent = math.floatExponentMin(T);
87 const max_exponent = math.floatExponentMax(T);
88 const exponent_bias = max_exponent;
89
90 // basic usage
91 try expect(ldexp(@as(T, 1.5), 4) == 24.0);
92
93 // normals -> subnormals
94 try expect(math.isNormal(ldexp(@as(T, 1.0), min_exponent)));
95 try expect(!math.isNormal(ldexp(@as(T, 1.0), min_exponent - 1)));
96
97 // normals -> zero
98 try expect(ldexp(@as(T, 1.0), min_exponent - fractional_bits) > 0.0);
99 try expect(ldexp(@as(T, 1.0), min_exponent - fractional_bits - 1) == 0.0);
100
101 // subnormals -> zero
102 try expect(ldexp(math.floatTrueMin(T), 0) > 0.0);
103 try expect(ldexp(math.floatTrueMin(T), -1) == 0.0);
104
105 // Multiplications might flush the denormals to zero, esp. at
106 // runtime, so we manually construct the constants here instead.
107 const Z = std.meta.Int(.unsigned, @bitSizeOf(T));
108 const EightTimesTrueMin = @bitCast(T, @as(Z, 8));
109 const TwoTimesTrueMin = @bitCast(T, @as(Z, 2));
110
111 // subnormals -> subnormals
112 try expect(ldexp(math.floatTrueMin(T), 3) == EightTimesTrueMin);
113 try expect(ldexp(EightTimesTrueMin, -2) == TwoTimesTrueMin);
114 try expect(ldexp(EightTimesTrueMin, -3) == math.floatTrueMin(T));
115
116 // subnormals -> normals (+)
117 try expect(ldexp(math.floatTrueMin(T), fractional_bits) == math.floatMin(T));
118 try expect(ldexp(math.floatTrueMin(T), fractional_bits - 1) == math.floatMin(T) * 0.5);
119
120 // subnormals -> normals (-)
121 try expect(ldexp(-math.floatTrueMin(T), fractional_bits) == -math.floatMin(T));
122 try expect(ldexp(-math.floatTrueMin(T), fractional_bits - 1) == -math.floatMin(T) * 0.5);
123
124 // subnormals -> float limits (+inf)
125 try expect(math.isFinite(ldexp(math.floatTrueMin(T), max_exponent + exponent_bias + fractional_bits - 1)));
126 try expect(ldexp(math.floatTrueMin(T), max_exponent + exponent_bias + fractional_bits) == math.inf(T));
127
128 // subnormals -> float limits (-inf)
129 try expect(math.isFinite(ldexp(-math.floatTrueMin(T), max_exponent + exponent_bias + fractional_bits - 1)));
130 try expect(ldexp(-math.floatTrueMin(T), max_exponent + exponent_bias + fractional_bits) == -math.inf(T));
131
132 // infinity -> infinity
133 try expect(ldexp(math.inf(T), math.maxInt(i32)) == math.inf(T));
134 try expect(ldexp(math.inf(T), math.minInt(i32)) == math.inf(T));
135 try expect(ldexp(math.inf(T), max_exponent) == math.inf(T));
136 try expect(ldexp(math.inf(T), min_exponent) == math.inf(T));
137 try expect(ldexp(-math.inf(T), math.maxInt(i32)) == -math.inf(T));
138 try expect(ldexp(-math.inf(T), math.minInt(i32)) == -math.inf(T));
139
140 // extremely large n
141 try expect(ldexp(math.floatMax(T), math.maxInt(i32)) == math.inf(T));
142 try expect(ldexp(math.floatMax(T), -math.maxInt(i32)) == 0.0);
143 try expect(ldexp(math.floatMax(T), math.minInt(i32)) == 0.0);
144 try expect(ldexp(math.floatTrueMin(T), math.maxInt(i32)) == math.inf(T));
145 try expect(ldexp(math.floatTrueMin(T), -math.maxInt(i32)) == 0.0);
146 try expect(ldexp(math.floatTrueMin(T), math.minInt(i32)) == 0.0);
147 }
91148}
test/c_abi/cfuncs.c+81
......@@ -2,6 +2,7 @@
22#include <stdlib.h>
33#include <stdbool.h>
44#include <string.h>
5#include <complex.h>
56
67void zig_panic();
78
......@@ -50,6 +51,13 @@ void zig_ptr(void *);
5051
5152void zig_bool(bool);
5253
54// Note: These two functions match the signature of __mulsc3 and __muldc3 in compiler-rt (and libgcc)
55float complex zig_cmultf_comp(float a_r, float a_i, float b_r, float b_i);
56double complex zig_cmultd_comp(double a_r, double a_i, double b_r, double b_i);
57
58float complex zig_cmultf(float complex a, float complex b);
59double complex zig_cmultd(double complex a, double complex b);
60
5361struct BigStruct {
5462 uint64_t a;
5563 uint64_t b;
......@@ -167,6 +175,43 @@ void run_c_tests(void) {
167175
168176 zig_bool(true);
169177
178 // TODO: Resolve https://github.com/ziglang/zig/issues/8465
179 //{
180 // float complex a = 1.25f + I * 2.6f;
181 // float complex b = 11.3f - I * 1.5f;
182 // float complex z = zig_cmultf(a, b);
183 // assert_or_panic(creal(z) == 1.5f);
184 // assert_or_panic(cimag(z) == 13.5f);
185 //}
186
187 {
188 double complex a = 1.25 + I * 2.6;
189 double complex b = 11.3 - I * 1.5;
190 double complex z = zig_cmultd(a, b);
191 assert_or_panic(creal(z) == 1.5);
192 assert_or_panic(cimag(z) == 13.5);
193 }
194
195 {
196 float a_r = 1.25f;
197 float a_i = 2.6f;
198 float b_r = 11.3f;
199 float b_i = -1.5f;
200 float complex z = zig_cmultf_comp(a_r, a_i, b_r, b_i);
201 assert_or_panic(creal(z) == 1.5f);
202 assert_or_panic(cimag(z) == 13.5f);
203 }
204
205 {
206 double a_r = 1.25;
207 double a_i = 2.6;
208 double b_r = 11.3;
209 double b_i = -1.5;
210 double complex z = zig_cmultd_comp(a_r, a_i, b_r, b_i);
211 assert_or_panic(creal(z) == 1.5);
212 assert_or_panic(cimag(z) == 13.5);
213 }
214
170215 {
171216 struct BigStruct s = {1, 2, 3, 4, 5};
172217 zig_big_struct(s);
......@@ -321,6 +366,42 @@ void c_five_floats(float a, float b, float c, float d, float e) {
321366 assert_or_panic(e == 5.0);
322367}
323368
369float complex c_cmultf_comp(float a_r, float a_i, float b_r, float b_i) {
370 assert_or_panic(a_r == 1.25f);
371 assert_or_panic(a_i == 2.6f);
372 assert_or_panic(b_r == 11.3f);
373 assert_or_panic(b_i == -1.5f);
374
375 return 1.5f + I * 13.5f;
376}
377
378double complex c_cmultd_comp(double a_r, double a_i, double b_r, double b_i) {
379 assert_or_panic(a_r == 1.25);
380 assert_or_panic(a_i == 2.6);
381 assert_or_panic(b_r == 11.3);
382 assert_or_panic(b_i == -1.5);
383
384 return 1.5 + I * 13.5;
385}
386
387float complex c_cmultf(float complex a, float complex b) {
388 assert_or_panic(creal(a) == 1.25f);
389 assert_or_panic(cimag(a) == 2.6f);
390 assert_or_panic(creal(b) == 11.3f);
391 assert_or_panic(cimag(b) == -1.5f);
392
393 return 1.5f + I * 13.5f;
394}
395
396double complex c_cmultd(double complex a, double complex b) {
397 assert_or_panic(creal(a) == 1.25);
398 assert_or_panic(cimag(a) == 2.6);
399 assert_or_panic(creal(b) == 11.3);
400 assert_or_panic(cimag(b) == -1.5);
401
402 return 1.5 + I * 13.5;
403}
404
324405void c_big_struct(struct BigStruct x) {
325406 assert_or_panic(x.a == 1);
326407 assert_or_panic(x.b == 2);
test/c_abi/main.zig+95
......@@ -145,6 +145,101 @@ export fn zig_bool(x: bool) void {
145145 expect(x) catch @panic("test failure: zig_bool");
146146}
147147
148// TODO: Replace these with the correct types once we resolve
149// https://github.com/ziglang/zig/issues/8465
150//
151// For now, we have no way of referring to the _Complex C types from Zig,
152// so our ABI is unavoidably broken on some platforms (such as i386)
153const ComplexFloat = extern struct {
154 real: f32,
155 imag: f32,
156};
157const ComplexDouble = extern struct {
158 real: f64,
159 imag: f64,
160};
161
162// Note: These two functions match the signature of __mulsc3 and __muldc3 in compiler-rt (and libgcc)
163extern fn c_cmultf_comp(a_r: f32, a_i: f32, b_r: f32, b_i: f32) ComplexFloat;
164extern fn c_cmultd_comp(a_r: f64, a_i: f64, b_r: f64, b_i: f64) ComplexDouble;
165
166extern fn c_cmultf(a: ComplexFloat, b: ComplexFloat) ComplexFloat;
167extern fn c_cmultd(a: ComplexDouble, b: ComplexDouble) ComplexDouble;
168
169test "C ABI complex float" {
170 if (true) return error.SkipZigTest; // See https://github.com/ziglang/zig/issues/8465
171
172 const a = ComplexFloat{ .real = 1.25, .imag = 2.6 };
173 const b = ComplexFloat{ .real = 11.3, .imag = -1.5 };
174
175 const z = c_cmultf(a, b);
176 expect(z.real == 1.5) catch @panic("test failure: zig_complex_float 1");
177 expect(z.imag == 13.5) catch @panic("test failure: zig_complex_float 2");
178}
179
180test "C ABI complex float by component" {
181 const a = ComplexFloat{ .real = 1.25, .imag = 2.6 };
182 const b = ComplexFloat{ .real = 11.3, .imag = -1.5 };
183
184 const z2 = c_cmultf_comp(a.real, a.imag, b.real, b.imag);
185 expect(z2.real == 1.5) catch @panic("test failure: zig_complex_float 3");
186 expect(z2.imag == 13.5) catch @panic("test failure: zig_complex_float 4");
187}
188
189test "C ABI complex double" {
190 const a = ComplexDouble{ .real = 1.25, .imag = 2.6 };
191 const b = ComplexDouble{ .real = 11.3, .imag = -1.5 };
192
193 const z = c_cmultd(a, b);
194 expect(z.real == 1.5) catch @panic("test failure: zig_complex_double 1");
195 expect(z.imag == 13.5) catch @panic("test failure: zig_complex_double 2");
196}
197
198test "C ABI complex double by component" {
199 const a = ComplexDouble{ .real = 1.25, .imag = 2.6 };
200 const b = ComplexDouble{ .real = 11.3, .imag = -1.5 };
201
202 const z = c_cmultd_comp(a.real, a.imag, b.real, b.imag);
203 expect(z.real == 1.5) catch @panic("test failure: zig_complex_double 3");
204 expect(z.imag == 13.5) catch @panic("test failure: zig_complex_double 4");
205}
206
207export fn zig_cmultf(a: ComplexFloat, b: ComplexFloat) ComplexFloat {
208 expect(a.real == 1.25) catch @panic("test failure: zig_cmultf 1");
209 expect(a.imag == 2.6) catch @panic("test failure: zig_cmultf 2");
210 expect(b.real == 11.3) catch @panic("test failure: zig_cmultf 3");
211 expect(b.imag == -1.5) catch @panic("test failure: zig_cmultf 4");
212
213 return .{ .real = 1.5, .imag = 13.5 };
214}
215
216export fn zig_cmultd(a: ComplexDouble, b: ComplexDouble) ComplexDouble {
217 expect(a.real == 1.25) catch @panic("test failure: zig_cmultd 1");
218 expect(a.imag == 2.6) catch @panic("test failure: zig_cmultd 2");
219 expect(b.real == 11.3) catch @panic("test failure: zig_cmultd 3");
220 expect(b.imag == -1.5) catch @panic("test failure: zig_cmultd 4");
221
222 return .{ .real = 1.5, .imag = 13.5 };
223}
224
225export fn zig_cmultf_comp(a_r: f32, a_i: f32, b_r: f32, b_i: f32) ComplexFloat {
226 expect(a_r == 1.25) catch @panic("test failure: zig_cmultf_comp 1");
227 expect(a_i == 2.6) catch @panic("test failure: zig_cmultf_comp 2");
228 expect(b_r == 11.3) catch @panic("test failure: zig_cmultf_comp 3");
229 expect(b_i == -1.5) catch @panic("test failure: zig_cmultf_comp 4");
230
231 return .{ .real = 1.5, .imag = 13.5 };
232}
233
234export fn zig_cmultd_comp(a_r: f64, a_i: f64, b_r: f64, b_i: f64) ComplexDouble {
235 expect(a_r == 1.25) catch @panic("test failure: zig_cmultd_comp 1");
236 expect(a_i == 2.6) catch @panic("test failure: zig_cmultd_comp 2");
237 expect(b_r == 11.3) catch @panic("test failure: zig_cmultd_comp 3");
238 expect(b_i == -1.5) catch @panic("test failure: zig_cmultd_comp 4");
239
240 return .{ .real = 1.5, .imag = 13.5 };
241}
242
148243const BigStruct = extern struct {
149244 a: u64,
150245 b: u64,
test/standalone/c_compiler/test.c+26
......@@ -1,4 +1,5 @@
11#include <assert.h>
2#include <complex.h>
23#include <stdio.h>
34#include <stdlib.h>
45
......@@ -24,5 +25,30 @@ int main (int argc, char *argv[])
2425
2526 if (!ok) abort();
2627
28 // Test some basic arithmetic from compiler-rt
29 {
30 double complex z = 0.0 + I * 4.0;
31 double complex w = 0.0 + I * 16.0;
32 double complex product = z * w;
33 double complex quotient = z / w;
34
35 if (!(creal(product) == -64.0)) abort();
36 if (!(cimag(product) == 0.0)) abort();
37 if (!(creal(quotient) == 0.25)) abort();
38 if (!(cimag(quotient) == 0.0)) abort();
39 }
40
41 {
42 float complex z = 4.0 + I * 4.0;
43 float complex w = 2.0 - I * 2.0;
44 float complex product = z * w;
45 float complex quotient = z / w;
46
47 if (!(creal(product) == 16.0)) abort();
48 if (!(cimag(product) == 0.0)) abort();
49 if (!(creal(quotient) == 0.0)) abort();
50 if (!(cimag(quotient) == 2.0)) abort();
51 }
52
2753 return EXIT_SUCCESS;
2854}