authorgravatar for jay@jayschwa.netJay Petacat <jay@jayschwa.net> 2026-03-12 00:21:00-06:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-12 21:05:22+01:00
log01cc1a58675806b72580e094609f8dde0019d6ea
treec51948fc758e2cdee2f0a8ba06acd9c0654f46bb
parent499aba9ca6eb3febfb16261b507145a39598c0b4

std.math.sign: Return smallest integer type that fits possible values

This may be a breaking change for callers that depend on the return type size being identical to the input. For example, `sign(i32) * i8 + i8` could now overflow. Callers can add an explicit type annotation to avoid that problem.

1 files changed, 21 insertions(+), 14 deletions(-)

lib/std/math.zig+21-14
......@@ -1772,26 +1772,33 @@ pub const F80 = struct {
17721772 }
17731773};
17741774
1775fn SignOf(T: type) type {
1776 return switch (@typeInfo(T)) {
1777 .comptime_int, .comptime_float => comptime_int,
1778 .int => |int| switch (int.signedness) {
1779 .signed => IntFittingRange(-1, 1),
1780 .unsigned => IntFittingRange(0, 1),
1781 },
1782 .float => IntFittingRange(-1, 1),
1783 .vector => |vec| @Vector(vec.len, SignOf(vec.child)),
1784 else => @compileError("Expected an int, float, or a vector of one, found " ++ @typeName(T)),
1785 };
1786}
1787
17751788/// Returns -1, 0, or 1.
17761789/// Supports integer and float types and vectors of integer and float types.
17771790/// Unsigned integer types will always return 0 or 1.
1791/// The returned integer type is the smallest that fits the possible values.
17781792/// Branchless.
1779pub inline fn sign(i: anytype) @TypeOf(i) {
1780 const T = @TypeOf(i);
1793pub inline fn sign(n: anytype) SignOf(@TypeOf(n)) {
1794 const T = SignOf(@TypeOf(n));
17811795 return switch (@typeInfo(T)) {
1782 .int, .comptime_int => @as(T, @intFromBool(i > 0)) - @as(T, @intFromBool(i < 0)),
1783 .float, .comptime_float => @as(T, @floatFromInt(@intFromBool(i > 0))) - @as(T, @floatFromInt(@intFromBool(i < 0))),
1784 .vector => |vinfo| blk: {
1785 switch (@typeInfo(vinfo.child)) {
1786 .int, .float => {
1787 const zero: T = @splat(0);
1788 const one: T = @splat(1);
1789 break :blk @select(vinfo.child, i > zero, one, zero) - @select(vinfo.child, i < zero, one, zero);
1790 },
1791 else => @compileError("Expected vector of ints or floats, found " ++ @typeName(T)),
1792 }
1796 .vector => |vec| blk: {
1797 const zero: T = @splat(0);
1798 const one: T = @splat(1);
1799 break :blk @select(vec.child, n > zero, one, zero) - @select(vec.child, n < zero, one, zero);
17931800 },
1794 else => @compileError("Expected an int, float or vector of one, found " ++ @typeName(T)),
1801 else => @as(T, @intFromBool(n > 0)) - @as(T, @intFromBool(n < 0)),
17951802 };
17961803}
17971804