-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
std.math.sign: get the sign of a int, float or vector #10281
Conversation
travisstaloch
commented
Dec 6, 2021
- returns -1, 0 or 1
- supports vector types
- branchless
fece009
to
42f51ab
Compare
|
As long as it's switching on type info, might as well add an implementation for floats too :P |
|
added float support. left a TODO for vectors of floats which is blocked by @inttofloat support for vector types. |
c04b7ec
to
523f5f2
Compare
|
looks like ci failure was just a timeout |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks solid, but tests should be improved for the quirky floating numbers and small integers.
lib/std/math.zig
Outdated
| @@ -1484,3 +1484,66 @@ test "boolMask" { | |||
| pub fn comptimeMod(num: anytype, denom: comptime_int) IntFittingRange(0, denom - 1) { | |||
| return @intCast(IntFittingRange(0, denom - 1), @mod(num, denom)); | |||
| } | |||
|
|
|||
| /// returns -1, 0 or 1. supports integer types, vectors of integer types, and float types | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: can you improve this to say what -1, 0 and 1 mean for the documentation?
lib/std/math.zig
Outdated
|
|
||
| test "sign" { | ||
| inline for ([_]type{ i8, i32, i64, u8, f16, f32, f64 }) |T| { | ||
| const input_expecteds = [_][2]comptime_int{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/input_expecteds/input_expected
lib/std/math.zig
Outdated
| } | ||
| } | ||
| } | ||
| comptime { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is sign comptime-only? Otherwise you want comptime + runtime tests. You can put them in a function and call one at comptime and the other at runtime.
lib/std/math.zig
Outdated
| } | ||
|
|
||
| test "sign" { | ||
| inline for ([_]type{ i8, i32, i64, u8, f16, f32, f64 }) |T| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You may want to also test these numbers: i0, i1, i2 and u0,u1,u2
lib/std/math.zig
Outdated
| comptime { | ||
| try std.testing.expectEqual(-1, sign(-10)); | ||
| try std.testing.expectEqual(1, sign(10)); | ||
| try std.testing.expectEqual(0, sign(0)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want to add more edge cases:
Add -0.0, 0.0 for floats (yes -0.0 is a valid floating point number), -inf, inf and NaN from here. Or mention the behavior in tests.
If you want to go the extra mile, add 2 denormal numbers (+ and -), but it should be fine without.
|
I struggle to see a use case for this function
|
|
In order to solve the float confusion, the function behavior can be changed as follows:
Behavior inspired from rust and java strictmath |
|
Sign as defined originally (0.0 and -0.0 return 0) is a common primitive in shading languages. (Though metal additionally specifies that sign(-0.0) is -0.0). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have taken the liberty to squash your 3 commits into 1 and rebase against latest master.
I would be happy to add std.math.sign as you have defined it here, but I would like the following enhancements first:
- avoid using
inline forin unit tests - use
inlineon the sign function, since "branchless" is one of the features
|
Thanks @andrewrk. I changed the loops from inline to comptime and made math.sign an inline function. Hope this is what you meant. Let me know if this needs additional changes. 👍 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well now it's only being tested at compile time, and not getting as much runtime coverage. Sorry for not being clear, what I mean is to avoid using loops entirely in your unit tests. This makes it easy to see exactly what cases are being tested, and comment some of them out, stack traces become more useful, etc.
Get rid of input_expecteds. Get rid of the list of types. Just hard code every test case directly, and don't over engineer it. You can use function calls to simplify the logic.
Here's an example of what I am looking for:
Lines 778 to 809 in 090461a
| test "divCeil" { | |
| try testDivCeil(); | |
| comptime try testDivCeil(); | |
| } | |
| fn testDivCeil() !void { | |
| try testing.expectEqual(@as(i32, 2), divCeil(i32, 5, 3) catch unreachable); | |
| try testing.expectEqual(@as(i32, -1), divCeil(i32, -5, 3) catch unreachable); | |
| try testing.expectEqual(@as(i32, -1), divCeil(i32, 5, -3) catch unreachable); | |
| try testing.expectEqual(@as(i32, 2), divCeil(i32, -5, -3) catch unreachable); | |
| try testing.expectEqual(@as(i32, 0), divCeil(i32, 0, 5) catch unreachable); | |
| try testing.expectEqual(@as(u32, 0), divCeil(u32, 0, 5) catch unreachable); | |
| try testing.expectError(error.DivisionByZero, divCeil(i8, -5, 0)); | |
| try testing.expectError(error.Overflow, divCeil(i8, -128, -1)); | |
| try testing.expectEqual(@as(f32, 0.0), divCeil(f32, 0.0, 5.0) catch unreachable); | |
| try testing.expectEqual(@as(f32, 2.0), divCeil(f32, 5.0, 3.0) catch unreachable); | |
| try testing.expectEqual(@as(f32, -1.0), divCeil(f32, -5.0, 3.0) catch unreachable); | |
| try testing.expectEqual(@as(f32, -1.0), divCeil(f32, 5.0, -3.0) catch unreachable); | |
| try testing.expectEqual(@as(f32, 2.0), divCeil(f32, -5.0, -3.0) catch unreachable); | |
| try testing.expectEqual(6, divCeil(comptime_int, 23, 4) catch unreachable); | |
| try testing.expectEqual(-5, divCeil(comptime_int, -23, 4) catch unreachable); | |
| try testing.expectEqual(-5, divCeil(comptime_int, 23, -4) catch unreachable); | |
| try testing.expectEqual(6, divCeil(comptime_int, -23, -4) catch unreachable); | |
| try testing.expectError(error.DivisionByZero, divCeil(comptime_int, 23, 0)); | |
| try testing.expectEqual(6.0, divCeil(comptime_float, 23.0, 4.0) catch unreachable); | |
| try testing.expectEqual(-5.0, divCeil(comptime_float, -23.0, 4.0) catch unreachable); | |
| try testing.expectEqual(-5.0, divCeil(comptime_float, 23.0, -4.0) catch unreachable); | |
| try testing.expectEqual(6.0, divCeil(comptime_float, -23.0, -4.0) catch unreachable); | |
| try testing.expectError(error.DivisionByZero, divCeil(comptime_float, 23.0, 0.0)); | |
| } |
One final request: please make this only a single commit, to make merging easier for me 🙏
|
btw isn't this just |
853d9ca
to
b81ff35
Compare
|
Apologies for my sloppy git skills. I did my best to 'squash' them into one.
Not sure about using copysign. First i've heard of it. Does it support vectors? If you think that would be better to use for float non-vectors, sounds good to me. |
The range of signum is {-1, 0, 1} while copysign(1, x) will just return -1 or +1 depending on sign. Copysign right now is unimplemented for integers and vectors too. |
|
@iddev5 I think all the math functions should be improved to take |
|
One more things is that signum would be defined different for float values than int values. See discussion above. Sadly different languages implement it differently (that behavior of +0 and -0). This is not something which signum suffers from in mathematics. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is good to merge once the CI is green. Note to person merging: upon merging fix the commit message