| author | |
| committer | |
| log | c3d67c5c4e2e86b472ae046f5e0e39db61009213 |
| tree | ee9c747780c625a4fc18badecd868ef67cf7d51f |
| parent | b316c25cc6f5b1703d7912da16c5c987f4406451 |
| parent | a06185f3620953b8402b26a12e80aee12cd9ac8d |
| signature |
compiler-rt: Implement complex multiply/division23 files changed, 900 insertions(+), 197 deletions(-)
lib/compiler_rt.zig+15-1| ... | ... | @@ -15,11 +15,25 @@ comptime { |
| 15 | 15 | _ = @import("compiler_rt/subxf3.zig"); |
| 16 | 16 | |
| 17 | 17 | _ = @import("compiler_rt/mulf3.zig"); |
| 18 | _ = @import("compiler_rt/muldf3.zig"); | |
| 19 | 18 | _ = @import("compiler_rt/mulsf3.zig"); |
| 19 | _ = @import("compiler_rt/muldf3.zig"); | |
| 20 | 20 | _ = @import("compiler_rt/multf3.zig"); |
| 21 | 21 | _ = @import("compiler_rt/mulxf3.zig"); |
| 22 | 22 | |
| 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 | ||
| 23 | 37 | _ = @import("compiler_rt/negsf2.zig"); |
| 24 | 38 | _ = @import("compiler_rt/negdf2.zig"); |
| 25 | 39 | _ = @import("compiler_rt/negtf2.zig"); |
lib/compiler_rt/divc3.zig created+62| ... | ... | @@ -0,0 +1,62 @@ |
| 1 | const std = @import("std"); | |
| 2 | const isNan = std.math.isNan; | |
| 3 | const isInf = std.math.isInf; | |
| 4 | const scalbn = std.math.scalbn; | |
| 5 | const ilogb = std.math.ilogb; | |
| 6 | const max = std.math.max; | |
| 7 | const fabs = std.math.fabs; | |
| 8 | const maxInt = std.math.maxInt; | |
| 9 | const minInt = std.math.minInt; | |
| 10 | const isFinite = std.math.isFinite; | |
| 11 | const copysign = std.math.copysign; | |
| 12 | const Complex = @import("mulc3.zig").Complex; | |
| 13 | ||
| 14 | /// Implementation based on Annex G of C17 Standard (N2176) | |
| 15 | pub 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | const math = std.math; | |
| 3 | const expect = std.testing.expect; | |
| 4 | ||
| 5 | const Complex = @import("./mulc3.zig").Complex; | |
| 6 | const __divhc3 = @import("./divhc3.zig").__divhc3; | |
| 7 | const __divsc3 = @import("./divsc3.zig").__divsc3; | |
| 8 | const __divdc3 = @import("./divdc3.zig").__divdc3; | |
| 9 | const __divxc3 = @import("./divxc3.zig").__divxc3; | |
| 10 | const __divtc3 = @import("./divtc3.zig").__divtc3; | |
| 11 | ||
| 12 | test { | |
| 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 | ||
| 20 | fn 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 @@ |
| 1 | const common = @import("./common.zig"); | |
| 2 | const divc3 = @import("./divc3.zig"); | |
| 3 | const Complex = @import("./mulc3.zig").Complex; | |
| 4 | ||
| 5 | comptime { | |
| 6 | @export(__divdc3, .{ .name = "__divdc3", .linkage = common.linkage }); | |
| 7 | } | |
| 8 | ||
| 9 | pub 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 @@ |
| 1 | const common = @import("./common.zig"); | |
| 2 | const divc3 = @import("./divc3.zig"); | |
| 3 | const Complex = @import("./mulc3.zig").Complex; | |
| 4 | ||
| 5 | comptime { | |
| 6 | @export(__divhc3, .{ .name = "__divhc3", .linkage = common.linkage }); | |
| 7 | } | |
| 8 | ||
| 9 | pub 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 @@ |
| 1 | const common = @import("./common.zig"); | |
| 2 | const divc3 = @import("./divc3.zig"); | |
| 3 | const Complex = @import("./mulc3.zig").Complex; | |
| 4 | ||
| 5 | comptime { | |
| 6 | @export(__divsc3, .{ .name = "__divsc3", .linkage = common.linkage }); | |
| 7 | } | |
| 8 | ||
| 9 | pub 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 @@ |
| 1 | const common = @import("./common.zig"); | |
| 2 | const divc3 = @import("./divc3.zig"); | |
| 3 | const Complex = @import("./mulc3.zig").Complex; | |
| 4 | ||
| 5 | comptime { | |
| 6 | @export(__divtc3, .{ .name = "__divtc3", .linkage = common.linkage }); | |
| 7 | } | |
| 8 | ||
| 9 | pub 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 @@ |
| 1 | const common = @import("./common.zig"); | |
| 2 | const divc3 = @import("./divc3.zig"); | |
| 3 | const Complex = @import("./mulc3.zig").Complex; | |
| 4 | ||
| 5 | comptime { | |
| 6 | @export(__divxc3, .{ .name = "__divxc3", .linkage = common.linkage }); | |
| 7 | } | |
| 8 | ||
| 9 | pub 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 { |
| 7 | 7 | @export(__extenddfxf2, .{ .name = "__extenddfxf2", .linkage = common.linkage }); |
| 8 | 8 | } |
| 9 | 9 | |
| 10 | fn __extenddfxf2(a: f64) callconv(.C) f80 { | |
| 10 | pub fn __extenddfxf2(a: f64) callconv(.C) f80 { | |
| 11 | 11 | return extend_f80(f64, @bitCast(u64, a)); |
| 12 | 12 | } |
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 |
| 92 | 92 | const src_qnan = 1 << (src_sig_bits - 1); |
| 93 | 93 | const src_nan_code = src_qnan - 1; |
| 94 | 94 | |
| 95 | const SrcShift = std.math.Log2Int(src_rep_t); | |
| 96 | ||
| 95 | 97 | var dst: std.math.F80 = undefined; |
| 96 | 98 | |
| 97 | 99 | // 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 |
| 124 | 126 | |
| 125 | 127 | dst.fraction = @as(u64, a_abs) << @intCast(u6, dst_sig_bits - src_sig_bits + scale); |
| 126 | 128 | 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)); | |
| 128 | 130 | dst.exp ^= 1; |
| 129 | 131 | dst.exp |= dst_exp_bias - src_exp_bias - scale + 1; |
| 130 | 132 | } else { |
lib/compiler_rt/extendf_test.zig+48| ... | ... | @@ -1,10 +1,27 @@ |
| 1 | const std = @import("std"); | |
| 2 | const math = std.math; | |
| 1 | 3 | const builtin = @import("builtin"); |
| 2 | 4 | const __extendhfsf2 = @import("extendhfsf2.zig").__extendhfsf2; |
| 3 | 5 | const __extendhftf2 = @import("extendhftf2.zig").__extendhftf2; |
| 4 | 6 | const __extendsftf2 = @import("extendsftf2.zig").__extendsftf2; |
| 5 | 7 | const __extenddftf2 = @import("extenddftf2.zig").__extenddftf2; |
| 8 | const __extenddfxf2 = @import("extenddfxf2.zig").__extenddfxf2; | |
| 6 | 9 | const F16T = @import("./common.zig").F16T; |
| 7 | 10 | |
| 11 | fn 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 | ||
| 8 | 25 | fn test__extenddftf2(a: f64, expected_hi: u64, expected_lo: u64) !void { |
| 9 | 26 | const x = __extenddftf2(a); |
| 10 | 27 | |
| ... | ... | @@ -65,6 +82,33 @@ fn test__extendsftf2(a: f32, expected_hi: u64, expected_lo: u64) !void { |
| 65 | 82 | return error.TestFailure; |
| 66 | 83 | } |
| 67 | 84 | |
| 85 | test "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 | ||
| 68 | 112 | test "extenddftf2" { |
| 69 | 113 | // qNaN |
| 70 | 114 | try test__extenddftf2(makeQNaN64(), 0x7fff800000000000, 0x0); |
| ... | ... | @@ -85,6 +129,10 @@ test "extenddftf2" { |
| 85 | 129 | try test__extenddftf2(0x1.23456789abcdefp+45, 0x402c23456789abcd, 0xf000000000000000); |
| 86 | 130 | |
| 87 | 131 | 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); | |
| 88 | 136 | } |
| 89 | 137 | |
| 90 | 138 | test "extendhfsf2" { |
lib/compiler_rt/mulc3.zig created+79| ... | ... | @@ -0,0 +1,79 @@ |
| 1 | const std = @import("std"); | |
| 2 | const isNan = std.math.isNan; | |
| 3 | const isInf = std.math.isInf; | |
| 4 | const copysign = std.math.copysign; | |
| 5 | ||
| 6 | pub 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) | |
| 14 | pub 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | const math = std.math; | |
| 3 | const expect = std.testing.expect; | |
| 4 | ||
| 5 | const Complex = @import("./mulc3.zig").Complex; | |
| 6 | const __mulhc3 = @import("./mulhc3.zig").__mulhc3; | |
| 7 | const __mulsc3 = @import("./mulsc3.zig").__mulsc3; | |
| 8 | const __muldc3 = @import("./muldc3.zig").__muldc3; | |
| 9 | const __mulxc3 = @import("./mulxc3.zig").__mulxc3; | |
| 10 | const __multc3 = @import("./multc3.zig").__multc3; | |
| 11 | ||
| 12 | test { | |
| 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 | ||
| 20 | fn 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 @@ |
| 1 | const common = @import("./common.zig"); | |
| 2 | const mulc3 = @import("./mulc3.zig"); | |
| 3 | ||
| 4 | pub const panic = common.panic; | |
| 5 | ||
| 6 | comptime { | |
| 7 | @export(__muldc3, .{ .name = "__muldc3", .linkage = common.linkage }); | |
| 8 | } | |
| 9 | ||
| 10 | pub 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 @@ |
| 1 | const common = @import("./common.zig"); | |
| 2 | const mulc3 = @import("./mulc3.zig"); | |
| 3 | ||
| 4 | pub const panic = common.panic; | |
| 5 | ||
| 6 | comptime { | |
| 7 | @export(__mulhc3, .{ .name = "__mulhc3", .linkage = common.linkage }); | |
| 8 | } | |
| 9 | ||
| 10 | pub 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 @@ |
| 1 | const common = @import("./common.zig"); | |
| 2 | const mulc3 = @import("./mulc3.zig"); | |
| 3 | ||
| 4 | pub const panic = common.panic; | |
| 5 | ||
| 6 | comptime { | |
| 7 | @export(__mulsc3, .{ .name = "__mulsc3", .linkage = common.linkage }); | |
| 8 | } | |
| 9 | ||
| 10 | pub 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 @@ |
| 1 | const common = @import("./common.zig"); | |
| 2 | const mulc3 = @import("./mulc3.zig"); | |
| 3 | ||
| 4 | pub const panic = common.panic; | |
| 5 | ||
| 6 | comptime { | |
| 7 | @export(__multc3, .{ .name = "__multc3", .linkage = common.linkage }); | |
| 8 | } | |
| 9 | ||
| 10 | pub 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 @@ |
| 1 | const common = @import("./common.zig"); | |
| 2 | const mulc3 = @import("./mulc3.zig"); | |
| 3 | ||
| 4 | pub const panic = common.panic; | |
| 5 | ||
| 6 | comptime { | |
| 7 | @export(__mulxc3, .{ .name = "__mulxc3", .linkage = common.linkage }); | |
| 8 | } | |
| 9 | ||
| 10 | pub 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; |
| 15 | 15 | /// |
| 16 | 16 | /// Special Cases: |
| 17 | 17 | /// - ilogb(+-inf) = maxInt(i32) |
| 18 | /// - ilogb(0) = maxInt(i32) | |
| 19 | /// - ilogb(nan) = maxInt(i32) | |
| 18 | /// - ilogb(+-0) = minInt(i32) | |
| 19 | /// - ilogb(nan) = minInt(i32) | |
| 20 | 20 | pub fn ilogb(x: anytype) i32 { |
| 21 | 21 | 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); | |
| 28 | 23 | } |
| 29 | 24 | |
| 30 | // TODO: unify these implementations with generics | |
| 25 | pub const fp_ilogbnan = minInt(i32); | |
| 26 | pub const fp_ilogb0 = minInt(i32); | |
| 31 | 27 | |
| 32 | // NOTE: Should these be exposed publicly? | |
| 33 | const fp_ilogbnan = -1 - @as(i32, maxInt(u32) >> 1); | |
| 34 | const fp_ilogb0 = fp_ilogbnan; | |
| 28 | fn 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); | |
| 35 | 32 | |
| 36 | fn 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); | |
| 39 | 34 | |
| 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); | |
| 44 | 38 | |
| 45 | if (e == 0) { | |
| 46 | u <<= 9; | |
| 47 | if (u == 0) { | |
| 48 | math.raiseInvalid(); | |
| 49 | return fp_ilogb0; | |
| 50 | } | |
| 39 | const absMask = signBit - 1; | |
| 51 | 40 | |
| 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 | ||
| 72 | fn 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); | |
| 79 | 43 | |
| 80 | 44 | if (e == 0) { |
| 81 | u <<= 12; | |
| 82 | 45 | if (u == 0) { |
| 83 | 46 | math.raiseInvalid(); |
| 84 | 47 | return fp_ilogb0; |
| 85 | 48 | } |
| 86 | 49 | |
| 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)); | |
| 93 | 53 | } |
| 94 | 54 | |
| 95 | if (e == 0x7FF) { | |
| 55 | if (e == maxExponent) { | |
| 96 | 56 | 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); | |
| 102 | 60 | } |
| 103 | 61 | |
| 104 | return e - 0x3FF; | |
| 62 | return e - exponentBias; | |
| 105 | 63 | } |
| 106 | 64 | |
| 107 | fn 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; | |
| 65 | test "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)); | |
| 136 | 68 | } |
| 137 | 69 | |
| 138 | test "type dispatch" { | |
| 139 | try expect(ilogb(@as(f32, 0.2)) == ilogb32(0.2)); | |
| 140 | try expect(ilogb(@as(f64, 0.2)) == ilogb64(0.2)); | |
| 70 | test "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); | |
| 141 | 81 | } |
| 142 | 82 | |
| 143 | 83 | test "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); | |
| 150 | 94 | } |
| 151 | 95 | |
| 152 | 96 | test "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 | ||
| 110 | test "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); | |
| 159 | 122 | } |
| 160 | 123 | |
| 161 | 124 | test "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 | ||
| 138 | test "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); | |
| 168 | 143 | } |
| 169 | 144 | |
| 170 | 145 | test "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); | |
| 175 | 150 | } |
| 176 | 151 | |
| 177 | 152 | test "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 | ||
| 159 | test "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); | |
| 182 | 164 | } |
| 183 | 165 | |
| 184 | 166 | test "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); | |
| 189 | 171 | } |
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 | ||
| 7 | 1 | const std = @import("std"); |
| 8 | 2 | const math = std.math; |
| 3 | const Log2Int = std.math.Log2Int; | |
| 9 | 4 | const assert = std.debug.assert; |
| 10 | 5 | const expect = std.testing.expect; |
| 11 | 6 | |
| 12 | 7 | /// Returns x * 2^n. |
| 13 | 8 | pub 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); | |
| 18 | 10 | const TBits = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); |
| 19 | 11 | |
| 12 | const exponent_bits = math.floatExponentBits(T); | |
| 20 | 13 | 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); | |
| 42 | 41 | } |
| 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); | |
| 50 | 62 | } |
| 51 | } | |
| 52 | 63 | |
| 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 | } | |
| 54 | 67 | } |
| 55 | 68 | |
| 56 | 69 | test "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); | |
| 64 | 70 | |
| 65 | 71 | // 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)); | |
| 76 | 73 | try expect(ldexp(@as(f32, 0x1.3FFFFFp-1), -126 - 22) == math.floatTrueMin(f32)); |
| 77 | 74 | 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)); | |
| 78 | 76 | try expect(ldexp(@as(f128, 0x1.7FFFFFFFFFFFFFFFFFFFFFFFFFFFp-1), -16382 - 111) == math.floatTrueMin(f128)); |
| 79 | 77 | |
| 80 | // float limits | |
| 81 | 78 | try expect(ldexp(math.floatMax(f32), -128 - 149) > 0.0); |
| 82 | 79 | 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 | } | |
| 91 | 148 | } |
test/c_abi/cfuncs.c+81| ... | ... | @@ -2,6 +2,7 @@ |
| 2 | 2 | #include <stdlib.h> |
| 3 | 3 | #include <stdbool.h> |
| 4 | 4 | #include <string.h> |
| 5 | #include <complex.h> | |
| 5 | 6 | |
| 6 | 7 | void zig_panic(); |
| 7 | 8 | |
| ... | ... | @@ -50,6 +51,13 @@ void zig_ptr(void *); |
| 50 | 51 | |
| 51 | 52 | void zig_bool(bool); |
| 52 | 53 | |
| 54 | // Note: These two functions match the signature of __mulsc3 and __muldc3 in compiler-rt (and libgcc) | |
| 55 | float complex zig_cmultf_comp(float a_r, float a_i, float b_r, float b_i); | |
| 56 | double complex zig_cmultd_comp(double a_r, double a_i, double b_r, double b_i); | |
| 57 | ||
| 58 | float complex zig_cmultf(float complex a, float complex b); | |
| 59 | double complex zig_cmultd(double complex a, double complex b); | |
| 60 | ||
| 53 | 61 | struct BigStruct { |
| 54 | 62 | uint64_t a; |
| 55 | 63 | uint64_t b; |
| ... | ... | @@ -167,6 +175,43 @@ void run_c_tests(void) { |
| 167 | 175 | |
| 168 | 176 | zig_bool(true); |
| 169 | 177 | |
| 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 | ||
| 170 | 215 | { |
| 171 | 216 | struct BigStruct s = {1, 2, 3, 4, 5}; |
| 172 | 217 | zig_big_struct(s); |
| ... | ... | @@ -321,6 +366,42 @@ void c_five_floats(float a, float b, float c, float d, float e) { |
| 321 | 366 | assert_or_panic(e == 5.0); |
| 322 | 367 | } |
| 323 | 368 | |
| 369 | float 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 | ||
| 378 | double 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 | ||
| 387 | float 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 | ||
| 396 | double 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 | ||
| 324 | 405 | void c_big_struct(struct BigStruct x) { |
| 325 | 406 | assert_or_panic(x.a == 1); |
| 326 | 407 | assert_or_panic(x.b == 2); |
test/c_abi/main.zig+95| ... | ... | @@ -145,6 +145,101 @@ export fn zig_bool(x: bool) void { |
| 145 | 145 | expect(x) catch @panic("test failure: zig_bool"); |
| 146 | 146 | } |
| 147 | 147 | |
| 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) | |
| 153 | const ComplexFloat = extern struct { | |
| 154 | real: f32, | |
| 155 | imag: f32, | |
| 156 | }; | |
| 157 | const 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) | |
| 163 | extern fn c_cmultf_comp(a_r: f32, a_i: f32, b_r: f32, b_i: f32) ComplexFloat; | |
| 164 | extern fn c_cmultd_comp(a_r: f64, a_i: f64, b_r: f64, b_i: f64) ComplexDouble; | |
| 165 | ||
| 166 | extern fn c_cmultf(a: ComplexFloat, b: ComplexFloat) ComplexFloat; | |
| 167 | extern fn c_cmultd(a: ComplexDouble, b: ComplexDouble) ComplexDouble; | |
| 168 | ||
| 169 | test "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 | ||
| 180 | test "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 | ||
| 189 | test "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 | ||
| 198 | test "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 | ||
| 207 | export 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 | ||
| 216 | export 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 | ||
| 225 | export 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 | ||
| 234 | export 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 | ||
| 148 | 243 | const BigStruct = extern struct { |
| 149 | 244 | a: u64, |
| 150 | 245 | b: u64, |
test/standalone/c_compiler/test.c+26| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | 1 | #include <assert.h> |
| 2 | #include <complex.h> | |
| 2 | 3 | #include <stdio.h> |
| 3 | 4 | #include <stdlib.h> |
| 4 | 5 | |
| ... | ... | @@ -24,5 +25,30 @@ int main (int argc, char *argv[]) |
| 24 | 25 | |
| 25 | 26 | if (!ok) abort(); |
| 26 | 27 | |
| 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 | ||
| 27 | 53 | return EXIT_SUCCESS; |
| 28 | 54 | } |