lib/simd/src/sign.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

 1 const std = @import("std");
 2 
 3 pub fn signBit(comptime D: type) D.Vector {
 4     const U = UnsignedLane(D);
 5     const mask: U = @as(U, 1) << (@bitSizeOf(D.Lane) - 1);
 6     return @bitCast(@as(UnsignedVector(D), @splat(mask)));
 7 }
 8 
 9 pub fn copySign(comptime D: type, magnitude: D.Vector, sign: D.Vector) D.Vector {
10     validateFloat(D);
11     const mask: UnsignedVector(D) = @bitCast(signBit(D));
12     const magnitude_bits: UnsignedVector(D) = @bitCast(magnitude);
13     const sign_bits: UnsignedVector(D) = @bitCast(sign);
14     return @bitCast((magnitude_bits & ~mask) | (sign_bits & mask));
15 }
16 
17 pub fn copySignToAbs(comptime D: type, absolute: D.Vector, sign: D.Vector) D.Vector {
18     validateFloat(D);
19     const mask: UnsignedVector(D) = @bitCast(signBit(D));
20     const absolute_bits: UnsignedVector(D) = @bitCast(absolute);
21     const sign_bits: UnsignedVector(D) = @bitCast(sign);
22     return @bitCast(absolute_bits | (sign_bits & mask));
23 }
24 
25 pub fn broadcastSignBit(comptime D: type, value: D.Vector) D.Vector {
26     if (@typeInfo(D.Lane) != .int or
27         @typeInfo(D.Lane).int.signedness != .signed)
28     {
29         @compileError("broadcastSignBit requires signed integer lanes");
30     }
31     const Amount = @Vector(D.lane_count, std.math.Log2Int(UnsignedLane(D)));
32     const amount: Amount = @splat(@bitSizeOf(D.Lane) - 1);
33     return value >> amount;
34 }
35 
36 fn UnsignedLane(comptime D: type) type {
37     return @Int(.unsigned, @bitSizeOf(D.Lane));
38 }
39 
40 fn UnsignedVector(comptime D: type) type {
41     return @Vector(D.lane_count, UnsignedLane(D));
42 }
43 
44 fn validateFloat(comptime D: type) void {
45     if (@typeInfo(D.Lane) != .float) @compileError("copySign requires floating-point lanes");
46 }
47 
48 fn verifyBroadcast(comptime T: type) !void {
49     const simd = @import("root.zig");
50     const D = simd.FixedTag(T, 4);
51     const value: D.Vector = .{ 0, 1, -1, std.math.minInt(T) };
52     try std.testing.expect(@reduce(.And, broadcastSignBit(D, value) ==
53         @as(D.Vector, .{ 0, 0, -1, -1 })));
54 }
55 
56 fn verifyCopySign(comptime T: type) !void {
57     const simd = @import("root.zig");
58     const D = simd.FixedTag(T, 4);
59     const U = @Int(.unsigned, @bitSizeOf(T));
60     const UV = @Vector(4, U);
61     const sign_mask: U = @as(U, 1) << (@bitSizeOf(T) - 1);
62     const one_bits: U = @bitCast(@as(T, 1));
63     const signs: D.Vector = @bitCast(@as(UV, .{ 0, sign_mask, sign_mask, 0 }));
64     const magnitude: D.Vector = @splat(1);
65     const expected: UV = .{ one_bits, one_bits | sign_mask, one_bits | sign_mask, one_bits };
66     try std.testing.expect(@reduce(.And, @as(UV, @bitCast(copySign(D, magnitude, signs))) == expected));
67     try std.testing.expect(@reduce(.And, @as(UV, @bitCast(copySignToAbs(D, magnitude, signs))) == expected));
68 }
69 
70 test "Highway sign masks copy floating sign bits including negative zero" {
71     const simd = @import("root.zig");
72     const D = simd.FixedTag(f32, 4);
73     const U = simd.FixedTag(u32, 4);
74     const magnitude: D.Vector = @bitCast(@as(U.Vector, .{
75         0, 0x3f80_0000, 0xc000_0000, 0x7fc0_1234,
76     }));
77     const sign: D.Vector = @bitCast(@as(U.Vector, .{
78         0x8000_0000, 0, 0x8000_0000, 0,
79     }));
80     const expected: U.Vector = .{
81         0x8000_0000, 0x3f80_0000, 0xc000_0000, 0x7fc0_1234,
82     };
83     try std.testing.expect(@reduce(.And, @as(U.Vector, @bitCast(copySign(D, magnitude, sign))) == expected));
84     const absolute: D.Vector = @bitCast(@as(U.Vector, .{
85         0, 0x3f80_0000, 0x4000_0000, 0x7fc0_1234,
86     }));
87     try std.testing.expect(@reduce(.And, @as(U.Vector, @bitCast(copySignToAbs(D, absolute, sign))) == expected));
88 }
89 
90 test "Highway broadcast sign covers all signed lane widths" {
91     inline for (.{ i8, i16, i32, i64 }) |T| try verifyBroadcast(T);
92 }
93 
94 test "Highway copy sign covers every floating lane width" {
95     inline for (.{ f16, f32, f64 }) |T| try verifyCopySign(T);
96 }