tiny.simd.arithmetic
Defined in tiny.simd.
API (60)
Actions
Public operations.
absabsDiffaddaddSubclampdivmaskedAbsmaskedAbsOrmaskedAddmaskedAddOrmaskedDivmaskedDivOrmaskedMaxmaskedMaxOrmaskedMinOrmaskedModOrmaskedMulmaskedMulAddmaskedMulAddOrmaskedMulOrmaskedMulSubmaskedMulSubOrmaskedNegMulAddmaskedNegMulAddOrmaskedNegMulSubmaskedNegMulSubOrmaskedSatAddOrmaskedSatSubOrmaskedSaturatedAddmaskedSaturatedSubmaskedSubmaskedSubOrmaxmax128max128UppermaxMagnitudemaxNumberminmin128min128UpperminMagnitudeminNumbermodmulmulAddmulAddSubmulSubmulSubAddnegnegMulAddnegMulSubpairwiseAddpairwiseAdd128pairwiseSubpairwiseSub128saturatedAbssaturatedAddsaturatedNegsaturatedSubsub
Source
Source: lib/simd/src/arithmetic.zig
zig
const std = @import("std");pub fn add(comptime D: type, a: D.Vector, b: D.Vector) D.Vector { return if (comptime isInteger(D.Lane)) a +% b else a + b;}pub fn sub(comptime D: type, a: D.Vector, b: D.Vector) D.Vector { return if (comptime isInteger(D.Lane)) a -% b else a - b;}pub fn mul(comptime D: type, a: D.Vector, b: D.Vector) D.Vector { return if (comptime isInteger(D.Lane)) a *% b else a * b;}pub fn addSub(comptime D: type, a: D.Vector, b: D.Vector) D.Vector { var result: D.Vector = undefined; inline for (0..D.lane_count) |index| { result[index] = if (index & 1 == 0) subLane(D.Lane, a[index], b[index]) else addLane(D.Lane, a[index], b[index]); } return result;}pub fn neg(comptime D: type, value: D.Vector) D.Vector { return switch (@typeInfo(D.Lane)) { .int => |info| if (info.signedness == .signed) @as(D.Vector, @splat(0)) -% value else @compileError("neg requires signed integer or floating-point lanes"), .float => -value, else => unreachable, };}pub fn saturatedNeg(comptime D: type, value: D.Vector) D.Vector { requireSignedInteger(D.Lane, "saturatedNeg"); var result: D.Vector = undefined; inline for (0..D.lane_count) |index| { result[index] = if (value[index] == std.math.minInt(D.Lane)) std.math.maxInt(D.Lane) else -value[index]; } return result;}pub fn abs(comptime D: type, value: D.Vector) D.Vector { return switch (@typeInfo(D.Lane)) { .int => |info| if (info.signedness == .signed) absInteger(D, value) else @compileError("abs requires signed integer or floating-point lanes"), .float => @abs(value), else => unreachable, };}pub fn saturatedAbs(comptime D: type, value: D.Vector) D.Vector { requireSignedInteger(D.Lane, "saturatedAbs"); var result: D.Vector = undefined; inline for (0..D.lane_count) |index| { result[index] = if (value[index] == std.math.minInt(D.Lane)) std.math.maxInt(D.Lane) else if (value[index] < 0) -value[index] else value[index]; } return result;}pub fn absDiff(comptime D: type, a: D.Vector, b: D.Vector) D.Vector { if (comptime @typeInfo(D.Lane) == .float) return @abs(a - b); if (comptime @typeInfo(D.Lane) != .int) @compileError("absDiff requires numeric lanes"); const U = @Int(.unsigned, @bitSizeOf(D.Lane)); var result: D.Vector = undefined; inline for (0..D.lane_count) |index| { const a_bits: U = @bitCast(a[index]); const b_bits: U = @bitCast(b[index]); const difference = if (a[index] >= b[index]) a_bits -% b_bits else b_bits -% a_bits; result[index] = @bitCast(difference); } return result;}pub fn saturatedAdd(comptime D: type, a: D.Vector, b: D.Vector) D.Vector { requireInteger(D.Lane, "saturatedAdd"); var result: D.Vector = undefined; inline for (0..D.lane_count) |index| { result[index] = saturatedAddLane(D.Lane, a[index], b[index]); } return result;}pub fn saturatedSub(comptime D: type, a: D.Vector, b: D.Vector) D.Vector { requireInteger(D.Lane, "saturatedSub"); var result: D.Vector = undefined; inline for (0..D.lane_count) |index| { result[index] = saturatedSubLane(D.Lane, a[index], b[index]); } return result;}pub fn div(comptime D: type, a: D.Vector, b: D.Vector) D.Vector { if (comptime @typeInfo(D.Lane) == .float) return a / b; requireInteger(D.Lane, "div"); var result: D.Vector = undefined; inline for (0..D.lane_count) |index| { result[index] = safeDivLane(D.Lane, a[index], b[index]); } return result;}pub fn mod(comptime D: type, a: D.Vector, b: D.Vector) D.Vector { requireInteger(D.Lane, "mod"); var result: D.Vector = undefined; inline for (0..D.lane_count) |index| { result[index] = safeModLane(D.Lane, a[index], b[index]); } return result;}pub fn min(comptime D: type, a: D.Vector, b: D.Vector) D.Vector { return @select(D.Lane, a < b, a, b);}pub fn max(comptime D: type, a: D.Vector, b: D.Vector) D.Vector { return @select(D.Lane, a > b, a, b);}pub fn minNumber(comptime D: type, a: D.Vector, b: D.Vector) D.Vector { if (comptime @typeInfo(D.Lane) != .float) return min(D, a, b); const a_nan = a != a; const b_nan = b != b; return @select( D.Lane, a_nan, b, @select(D.Lane, b_nan, a, min(D, a, b)), );}pub fn maxNumber(comptime D: type, a: D.Vector, b: D.Vector) D.Vector { if (comptime @typeInfo(D.Lane) != .float) return max(D, a, b); const a_nan = a != a; const b_nan = b != b; return @select( D.Lane, a_nan, b, @select(D.Lane, b_nan, a, max(D, a, b)), );}pub fn minMagnitude(comptime D: type, a: D.Vector, b: D.Vector) D.Vector { return magnitudeChoice(D, a, b, false);}pub fn maxMagnitude(comptime D: type, a: D.Vector, b: D.Vector) D.Vector { return magnitudeChoice(D, a, b, true);}pub fn min128(comptime D: type, a: D.Vector, b: D.Vector) D.Vector { return choose128(D, a, b, false, false);}pub fn max128(comptime D: type, a: D.Vector, b: D.Vector) D.Vector { return choose128(D, a, b, true, false);}pub fn min128Upper(comptime D: type, a: D.Vector, b: D.Vector) D.Vector { return choose128(D, a, b, false, true);}pub fn max128Upper(comptime D: type, a: D.Vector, b: D.Vector) D.Vector { return choose128(D, a, b, true, true);}pub fn clamp( comptime D: type, value: D.Vector, lower: D.Vector, upper: D.Vector,) D.Vector { return max(D, lower, min(D, value, upper));}pub fn mulAdd( comptime D: type, multiplicand: D.Vector, multiplier: D.Vector, addend: D.Vector,) D.Vector { if (comptime isInteger(D.Lane)) { return add(D, mul(D, multiplicand, multiplier), addend); } return @mulAdd(D.Vector, multiplicand, multiplier, addend);}pub fn negMulAdd( comptime D: type, multiplicand: D.Vector, multiplier: D.Vector, addend: D.Vector,) D.Vector { if (comptime isInteger(D.Lane)) { return add(D, mul(D, neg(D, multiplicand), multiplier), addend); } return @mulAdd(D.Vector, -multiplicand, multiplier, addend);}pub fn mulSub( comptime D: type, multiplicand: D.Vector, multiplier: D.Vector, subtrahend: D.Vector,) D.Vector { if (comptime isInteger(D.Lane)) { return sub(D, mul(D, multiplicand, multiplier), subtrahend); } return @mulAdd(D.Vector, multiplicand, multiplier, -subtrahend);}pub fn negMulSub( comptime D: type, multiplicand: D.Vector, multiplier: D.Vector, subtrahend: D.Vector,) D.Vector { if (comptime isInteger(D.Lane)) { return sub(D, mul(D, neg(D, multiplicand), multiplier), subtrahend); } return @mulAdd(D.Vector, -multiplicand, multiplier, -subtrahend);}pub fn mulAddSub( comptime D: type, multiplicand: D.Vector, multiplier: D.Vector, addend: D.Vector,) D.Vector { return alternate(D, mulSub(D, multiplicand, multiplier, addend), mulAdd(D, multiplicand, multiplier, addend));}pub fn mulSubAdd( comptime D: type, multiplicand: D.Vector, multiplier: D.Vector, addend: D.Vector,) D.Vector { return alternate(D, mulAdd(D, multiplicand, multiplier, addend), mulSub(D, multiplicand, multiplier, addend));}pub fn maskedMinOr(comptime D: type, no: D.Vector, mask: D.Mask, a: D.Vector, b: D.Vector) D.Vector { return maskedOr(D, no, mask, min(D, a, b));}pub fn maskedMaxOr(comptime D: type, no: D.Vector, mask: D.Mask, a: D.Vector, b: D.Vector) D.Vector { return maskedOr(D, no, mask, max(D, a, b));}pub fn maskedAddOr(comptime D: type, no: D.Vector, mask: D.Mask, a: D.Vector, b: D.Vector) D.Vector { return maskedOr(D, no, mask, add(D, a, b));}pub fn maskedSubOr(comptime D: type, no: D.Vector, mask: D.Mask, a: D.Vector, b: D.Vector) D.Vector { return maskedOr(D, no, mask, sub(D, a, b));}pub fn maskedMulOr(comptime D: type, no: D.Vector, mask: D.Mask, a: D.Vector, b: D.Vector) D.Vector { return maskedOr(D, no, mask, mul(D, a, b));}pub fn maskedDivOr(comptime D: type, no: D.Vector, mask: D.Mask, a: D.Vector, b: D.Vector) D.Vector { return maskedOr(D, no, mask, div(D, a, b));}pub fn maskedModOr(comptime D: type, no: D.Vector, mask: D.Mask, a: D.Vector, b: D.Vector) D.Vector { return maskedOr(D, no, mask, mod(D, a, b));}pub fn maskedSatAddOr(comptime D: type, no: D.Vector, mask: D.Mask, a: D.Vector, b: D.Vector) D.Vector { return maskedOr(D, no, mask, saturatedAdd(D, a, b));}pub fn maskedSatSubOr(comptime D: type, no: D.Vector, mask: D.Mask, a: D.Vector, b: D.Vector) D.Vector { return maskedOr(D, no, mask, saturatedSub(D, a, b));}pub fn maskedAbsOr(comptime D: type, no: D.Vector, mask: D.Mask, value: D.Vector) D.Vector { return maskedOr(D, no, mask, abs(D, value));}pub fn maskedMulAddOr(comptime D: type, no: D.Vector, mask: D.Mask, a: D.Vector, b: D.Vector, c: D.Vector) D.Vector { return maskedOr(D, no, mask, mulAdd(D, a, b, c));}pub fn maskedMulSubOr(comptime D: type, no: D.Vector, mask: D.Mask, a: D.Vector, b: D.Vector, c: D.Vector) D.Vector { return maskedOr(D, no, mask, mulSub(D, a, b, c));}pub fn maskedNegMulAddOr(comptime D: type, no: D.Vector, mask: D.Mask, a: D.Vector, b: D.Vector, c: D.Vector) D.Vector { return maskedOr(D, no, mask, negMulAdd(D, a, b, c));}pub fn maskedNegMulSubOr(comptime D: type, no: D.Vector, mask: D.Mask, a: D.Vector, b: D.Vector, c: D.Vector) D.Vector { return maskedOr(D, no, mask, negMulSub(D, a, b, c));}pub fn maskedAbs(comptime D: type, mask: D.Mask, value: D.Vector) D.Vector { return maskedZero(D, mask, abs(D, value));}pub fn maskedMax(comptime D: type, mask: D.Mask, a: D.Vector, b: D.Vector) D.Vector { return maskedZero(D, mask, max(D, a, b));}pub fn maskedAdd(comptime D: type, mask: D.Mask, a: D.Vector, b: D.Vector) D.Vector { return maskedZero(D, mask, add(D, a, b));}pub fn maskedSub(comptime D: type, mask: D.Mask, a: D.Vector, b: D.Vector) D.Vector { return maskedZero(D, mask, sub(D, a, b));}pub fn maskedMul(comptime D: type, mask: D.Mask, a: D.Vector, b: D.Vector) D.Vector { return maskedZero(D, mask, mul(D, a, b));}pub fn maskedDiv(comptime D: type, mask: D.Mask, a: D.Vector, b: D.Vector) D.Vector { return maskedZero(D, mask, div(D, a, b));}pub fn maskedSaturatedAdd(comptime D: type, mask: D.Mask, a: D.Vector, b: D.Vector) D.Vector { return maskedZero(D, mask, saturatedAdd(D, a, b));}pub fn maskedSaturatedSub(comptime D: type, mask: D.Mask, a: D.Vector, b: D.Vector) D.Vector { return maskedZero(D, mask, saturatedSub(D, a, b));}pub fn maskedMulAdd(comptime D: type, mask: D.Mask, a: D.Vector, b: D.Vector, c: D.Vector) D.Vector { return maskedZero(D, mask, mulAdd(D, a, b, c));}pub fn maskedMulSub(comptime D: type, mask: D.Mask, a: D.Vector, b: D.Vector, c: D.Vector) D.Vector { return maskedZero(D, mask, mulSub(D, a, b, c));}pub fn maskedNegMulAdd(comptime D: type, mask: D.Mask, a: D.Vector, b: D.Vector, c: D.Vector) D.Vector { return maskedZero(D, mask, negMulAdd(D, a, b, c));}pub fn maskedNegMulSub(comptime D: type, mask: D.Mask, a: D.Vector, b: D.Vector, c: D.Vector) D.Vector { return maskedZero(D, mask, negMulSub(D, a, b, c));}pub fn pairwiseAdd(comptime D: type, a: D.Vector, b: D.Vector) D.Vector { if (D.lane_count < 2) @compileError("pairwiseAdd requires at least two lanes"); var result: D.Vector = undefined; inline for (0..D.lane_count / 2) |pair| { result[pair * 2] = addLane(D.Lane, a[pair * 2], a[pair * 2 + 1]); result[pair * 2 + 1] = addLane(D.Lane, b[pair * 2], b[pair * 2 + 1]); } return result;}pub fn pairwiseSub(comptime D: type, a: D.Vector, b: D.Vector) D.Vector { if (D.lane_count < 2) @compileError("pairwiseSub requires at least two lanes"); var result: D.Vector = undefined; inline for (0..D.lane_count / 2) |pair| { result[pair * 2] = subLane(D.Lane, a[pair * 2 + 1], a[pair * 2]); result[pair * 2 + 1] = subLane(D.Lane, b[pair * 2 + 1], b[pair * 2]); } return result;}pub fn pairwiseAdd128(comptime D: type, a: D.Vector, b: D.Vector) D.Vector { return pairwise128(D, false, a, b);}pub fn pairwiseSub128(comptime D: type, a: D.Vector, b: D.Vector) D.Vector { return pairwise128(D, true, a, b);}fn pairwise128(comptime D: type, comptime subtract: bool, a: D.Vector, b: D.Vector) D.Vector { if (D.byte_count < 16) @compileError("128-bit pairwise operations require at least one full block"); const lanes_per_block = 16 / @sizeOf(D.Lane); if (lanes_per_block < 2) @compileError("pairwise operations require at least two lanes per block"); var result: D.Vector = undefined; inline for (0..D.lane_count / lanes_per_block) |block| { inline for (0..lanes_per_block / 2) |pair| { const source = block * lanes_per_block + pair * 2; const destination = block * lanes_per_block + pair; result[destination] = if (subtract) subLane(D.Lane, a[source + 1], a[source]) else addLane(D.Lane, a[source], a[source + 1]); result[destination + lanes_per_block / 2] = if (subtract) subLane(D.Lane, b[source + 1], b[source]) else addLane(D.Lane, b[source], b[source + 1]); } } return result;}fn addLane(comptime T: type, a: T, b: T) T { return if (@typeInfo(T) == .int) a +% b else a + b;}fn subLane(comptime T: type, a: T, b: T) T { return if (@typeInfo(T) == .int) a -% b else a - b;}fn isInteger(comptime T: type) bool { return @typeInfo(T) == .int;}fn absInteger(comptime D: type, value: D.Vector) D.Vector { var result: D.Vector = undefined; inline for (0..D.lane_count) |index| { result[index] = if (value[index] < 0) 0 -% value[index] else value[index]; } return result;}fn saturatedAddLane(comptime T: type, a: T, b: T) T { return if (@typeInfo(T).int.signedness == .signed) blk: { const sum = @as(i128, a) + @as(i128, b); break :blk @intCast(std.math.clamp( sum, @as(i128, std.math.minInt(T)), @as(i128, std.math.maxInt(T)), )); } else blk: { const sum = @as(u128, a) + @as(u128, b); break :blk @intCast(@min(sum, @as(u128, std.math.maxInt(T)))); };}fn saturatedSubLane(comptime T: type, a: T, b: T) T { return if (@typeInfo(T).int.signedness == .signed) blk: { const difference = @as(i128, a) - @as(i128, b); break :blk @intCast(std.math.clamp( difference, @as(i128, std.math.minInt(T)), @as(i128, std.math.maxInt(T)), )); } else if (a < b) 0 else a - b;}fn safeDivLane(comptime T: type, a: T, b: T) T { if (b == 0) return 0; if (@typeInfo(T).int.signedness == .signed and a == std.math.minInt(T) and b == -1) { return 0; } return @divTrunc(a, b);}fn safeModLane(comptime T: type, a: T, b: T) T { if (b == 0) return 0; if (@typeInfo(T).int.signedness == .signed and a == std.math.minInt(T) and b == -1) { return 0; } return @rem(a, b);}fn magnitudeChoice( comptime D: type, a: D.Vector, b: D.Vector, comptime choose_maximum: bool,) D.Vector { var result: D.Vector = undefined; inline for (0..D.lane_count) |index| { const a_smaller = magnitudeLess(D.Lane, a[index], b[index]); result[index] = if (choose_maximum) (if (a_smaller) b[index] else a[index]) else (if (a_smaller) a[index] else b[index]); } return result;}fn magnitudeLess(comptime T: type, a: T, b: T) bool { return switch (@typeInfo(T)) { .float => blk: { const magnitude_a = @abs(a); const magnitude_b = @abs(b); break :blk magnitude_a < magnitude_b or (magnitude_a == magnitude_b and a < b); }, .int => |info| if (info.signedness == .unsigned) a < b else blk: { const U = @Int(.unsigned, @bitSizeOf(T)); const a_bits: U = @bitCast(a); const b_bits: U = @bitCast(b); const magnitude_a = if (a < 0) 0 -% a_bits else a_bits; const magnitude_b = if (b < 0) 0 -% b_bits else b_bits; break :blk magnitude_a < magnitude_b or (magnitude_a == magnitude_b and a < b); }, else => unreachable, };}fn choose128( comptime D: type, a: D.Vector, b: D.Vector, comptime choose_maximum: bool, comptime upper_only: bool,) D.Vector { if (D.Lane != u64 or D.lane_count < 2 or D.lane_count & 1 != 0) { @compileError("128-bit min/max requires an even number of u64 lanes"); } var result: D.Vector = undefined; inline for (0..D.lane_count / 2) |pair| { const low = pair * 2; const high = low + 1; const a_less = if (upper_only) a[high] < b[high] else a[high] < b[high] or (a[high] == b[high] and a[low] < b[low]); const a_greater = if (upper_only) a[high] > b[high] else a[high] > b[high] or (a[high] == b[high] and a[low] > b[low]); const take_a = if (choose_maximum) a_greater else a_less; result[low] = if (take_a) a[low] else b[low]; result[high] = if (take_a) a[high] else b[high]; } return result;}fn alternate(comptime D: type, even: D.Vector, odd: D.Vector) D.Vector { var result: D.Vector = undefined; inline for (0..D.lane_count) |index| { result[index] = if (index & 1 == 0) even[index] else odd[index]; } return result;}fn maskedOr(comptime D: type, no: D.Vector, mask: D.Mask, yes: D.Vector) D.Vector { return @select(D.Lane, mask, yes, no);}fn maskedZero(comptime D: type, mask: D.Mask, yes: D.Vector) D.Vector { return maskedOr(D, @as(D.Vector, @splat(0)), mask, yes);}fn requireInteger(comptime T: type, comptime operation: []const u8) void { if (@typeInfo(T) != .int) @compileError(operation ++ " requires integer lanes");}fn requireSignedInteger(comptime T: type, comptime operation: []const u8) void { if (@typeInfo(T) != .int or @typeInfo(T).int.signedness != .signed) { @compileError(operation ++ " requires signed integer lanes"); }}test "integer arithmetic wraps lane by lane" { const simd = @import("root.zig"); const D = simd.FixedTag(u8, 4); const a: D.Vector = .{ 255, 0, 200, 3 }; const b: D.Vector = .{ 1, 1, 2, 100 }; try std.testing.expect(@reduce(.And, add(D, a, b) == @as(D.Vector, .{ 0, 1, 202, 103 }))); try std.testing.expect(@reduce(.And, sub(D, a, b) == @as(D.Vector, .{ 254, 255, 198, 159 }))); try std.testing.expect(@reduce(.And, mul(D, a, b) == @as(D.Vector, .{ 255, 0, 144, 44 })));}test "floating arithmetic and clamp retain lane semantics" { const simd = @import("root.zig"); const D = simd.FixedTag(f32, 4); const a: D.Vector = .{ -2, 1, 4, 8 }; const b: D.Vector = @splat(2); try std.testing.expect(@reduce(.And, add(D, a, b) == @as(D.Vector, .{ 0, 3, 6, 10 }))); try std.testing.expect(@reduce(.And, clamp(D, a, @splat(0), @splat(5)) == @as(D.Vector, .{ 0, 1, 4, 5 })));}test "Highway pairwise arithmetic interleaves source folds" { const simd = @import("root.zig"); const D = simd.FixedTag(i16, 8); const a: D.Vector = .{ 1, 2, 3, 4, 5, 6, 7, 8 }; const b: D.Vector = .{ 10, 20, 30, 40, 50, 60, 70, 80 }; try std.testing.expect(@reduce(.And, pairwiseAdd(D, a, b) == @as(D.Vector, .{ 3, 30, 7, 70, 11, 110, 15, 150, }))); try std.testing.expect(@reduce(.And, pairwiseSub(D, a, b) == @as(D.Vector, .{ 1, 10, 1, 10, 1, 10, 1, 10, })));}test "Highway 128-bit pairwise arithmetic packs each source half per block" { const simd = @import("root.zig"); const D = simd.FixedTag(u32, 8); const a: D.Vector = .{ 1, 2, 3, 4, 5, 6, 7, 8 }; const b: D.Vector = .{ 10, 20, 30, 40, 50, 60, 70, 80 }; try std.testing.expect(@reduce(.And, pairwiseAdd128(D, a, b) == @as(D.Vector, .{ 3, 7, 30, 70, 11, 15, 110, 150, }))); try std.testing.expect(@reduce(.And, pairwiseSub128(D, a, b) == @as(D.Vector, .{ 1, 1, 10, 10, 1, 1, 10, 10, })));}fn verifyPairwiseLaneType(comptime T: type) !void { const simd = @import("root.zig"); const D = simd.FixedTag(T, @max(2, 16 / @sizeOf(T))); const value: D.Vector = @splat(0); try std.testing.expect(@reduce(.And, pairwiseAdd(D, value, value) == value)); try std.testing.expect(@reduce(.And, pairwiseSub(D, value, value) == value)); try std.testing.expect(@reduce(.And, pairwiseAdd128(D, value, value) == value)); try std.testing.expect(@reduce(.And, pairwiseSub128(D, value, value) == value));}test "Highway pairwise arithmetic instantiates every lane type" { inline for (.{ u8, i8, u16, i16, u32, i32, u64, i64, f16, f32, f64 }) |T| { try verifyPairwiseLaneType(T); }}test "Highway absolute saturating division and alternating arithmetic retain edge semantics" { const simd = @import("root.zig"); const D = simd.FixedTag(i16, 8); const a: D.Vector = .{ std.math.minInt(i16), -300, -7, -1, 0, 7, 300, std.math.maxInt(i16) }; const b: D.Vector = .{ -1, 1000, 3, 2, 0, -3, 1000, 1 }; try std.testing.expect(@reduce(.And, abs(D, a) == @as(D.Vector, .{ std.math.minInt(i16), 300, 7, 1, 0, 7, 300, std.math.maxInt(i16), }))); try std.testing.expect(@reduce(.And, saturatedAbs(D, a) == @as(D.Vector, .{ std.math.maxInt(i16), 300, 7, 1, 0, 7, 300, std.math.maxInt(i16), }))); try std.testing.expect(@reduce(.And, saturatedNeg(D, a) == @as(D.Vector, .{ std.math.maxInt(i16), 300, 7, 1, 0, -7, -300, -std.math.maxInt(i16), }))); try std.testing.expect(@reduce(.And, saturatedAdd(D, a, b) == @as(D.Vector, .{ std.math.minInt(i16), 700, -4, 1, 0, 4, 1300, std.math.maxInt(i16), }))); try std.testing.expect(@reduce(.And, saturatedSub(D, a, b) == @as(D.Vector, .{ -32767, -1300, -10, -3, 0, 10, -700, 32766, }))); try std.testing.expect(@reduce(.And, div(D, a, b) == @as(D.Vector, .{ 0, 0, -2, 0, 0, -2, 0, 32767, }))); try std.testing.expect(@reduce(.And, mod(D, a, b) == @as(D.Vector, .{ 0, -300, -1, -1, 0, 1, 300, 0, }))); try std.testing.expect(@reduce(.And, addSub(D, a, b) == @as(D.Vector, .{ -32767, 700, -10, 1, 0, 4, -700, -32768, })));}test "Highway absolute difference and saturation instantiate every integer lane" { const simd = @import("root.zig"); inline for (.{ u8, i8, u16, i16, u32, i32, u64, i64 }) |T| { const D = simd.FixedTag(T, 4); const zero: D.Vector = @splat(0); const one: D.Vector = @splat(1); _ = absDiff(D, zero, one); _ = saturatedAdd(D, zero, one); _ = saturatedSub(D, zero, one); _ = div(D, one, one); _ = mod(D, one, one); _ = maskedSaturatedAdd(D, @as(D.Mask, @splat(true)), zero, one); _ = maskedSaturatedSub(D, @as(D.Mask, @splat(true)), zero, one); }}test "Highway number and magnitude extrema handle NaN ties and signed minima" { const simd = @import("root.zig"); const F = simd.FixedTag(f32, 4); const nan_value = std.math.nan(f32); const a: F.Vector = .{ nan_value, 3, -4, -2 }; const b: F.Vector = .{ 7, nan_value, 2, 2 }; const minimum = minNumber(F, a, b); const maximum = maxNumber(F, a, b); try std.testing.expectEqual(@as(f32, 7), minimum[0]); try std.testing.expectEqual(@as(f32, 3), minimum[1]); try std.testing.expectEqual(@as(f32, -4), minimum[2]); try std.testing.expectEqual(@as(f32, -2), minimum[3]); try std.testing.expectEqual(@as(f32, 7), maximum[0]); try std.testing.expectEqual(@as(f32, 3), maximum[1]); const I = simd.FixedTag(i32, 4); const x: I.Vector = .{ std.math.minInt(i32), -7, -3, 5 }; const y: I.Vector = .{ std.math.maxInt(i32), 6, 3, -5 }; try std.testing.expect(@reduce(.And, minMagnitude(I, x, y) == @as(I.Vector, .{ std.math.maxInt(i32), 6, -3, -5, }))); try std.testing.expect(@reduce(.And, maxMagnitude(I, x, y) == @as(I.Vector, .{ std.math.minInt(i32), -7, 3, 5, })));}test "Highway 128-bit extrema compare complete pairs or upper keys" { const simd = @import("root.zig"); const D = simd.FixedTag(u64, 8); const a: D.Vector = .{ 9, 1, 7, 4, 10, 6, 20, 8 }; const b: D.Vector = .{ 10, 1, 8, 3, 11, 6, 19, 8 }; try std.testing.expect(@reduce(.And, min128(D, a, b) == @as(D.Vector, .{ 9, 1, 8, 3, 10, 6, 19, 8, }))); try std.testing.expect(@reduce(.And, max128(D, a, b) == @as(D.Vector, .{ 10, 1, 7, 4, 11, 6, 20, 8, }))); try std.testing.expect(@reduce(.And, min128Upper(D, a, b) == @as(D.Vector, .{ 10, 1, 8, 3, 11, 6, 19, 8, }))); try std.testing.expect(@reduce(.And, max128Upper(D, a, b) == @as(D.Vector, .{ 10, 1, 7, 4, 11, 6, 19, 8, })));}test "Highway fused and masked arithmetic alternate and merge exact lanes" { const simd = @import("root.zig"); const D = simd.FixedTag(f32, 4); const a: D.Vector = .{ 2, 3, 4, 5 }; const b: D.Vector = @splat(2); const c: D.Vector = @splat(1); try std.testing.expect(@reduce(.And, mulAddSub(D, a, b, c) == @as(D.Vector, .{ 3, 7, 7, 11 }))); try std.testing.expect(@reduce(.And, mulSubAdd(D, a, b, c) == @as(D.Vector, .{ 5, 5, 9, 9 }))); const mask: D.Mask = .{ true, false, true, false }; try std.testing.expect(@reduce(.And, maskedMulAddOr(D, @splat(9), mask, a, b, c) == @as(D.Vector, .{ 5, 9, 9, 9 }))); try std.testing.expect(@reduce(.And, maskedNegMulSub(D, mask, a, b, c) == @as(D.Vector, .{ -5, 0, -9, 0 })));}Source: lib/simd/src/root.zig:26
zig
pub const arithmetic = @import("arithmetic.zig");Audit
| Definitions | 1 |
|---|---|
| Public names | 1 |
| Members | 0 |
| Version | 26.7.0 |
| Revision | daab053ee433 |