Migrated from https://github.com/ziglang/zig/issues/19575
Original proposal below. It was accepted, discussed, and then un-accepted for re-evaluation, and is now open for comments and counter proposals.
A pull request partially implementing some of this will be landing imminently: #30906
Motivation
- Make using floating point values in game development less cumbersome. It should be low friction to convert a float to an int. A chained builtin is too much friction.
- Make the points where illegal behavior can possibly occur obvious.
- Delete
std.math.lossyCast. This functionality should be provided by the language.
Updated Builtins
These four functions have clarified definitions:
@round: Returns the nearest representable integer to the operand; away from zero in halfway case.
@trunc: Returns the nearest representable integer to the operand, towards zero.
@floor: Returns the nearest representable integer to the operand, towards negative infinity.
@ceil: Returns the nearest representable integer to the operand, towards positive infinity.
When the result type is non-integer, the behavior is the same as before. If the operand is nan or infinite, the operand is returned. In this case an integer operand is not allowed except where it would be allowed by @as.
When the result type is integer, these functions have a saturating effect. A value outside the integer range is clamped; nan is illegal. In this case an integer operand is allowed.
Conformance Tests
conststd=@import("std");constexpect=std.testing.expect;test"@round on integers"{varx:i32=1<<18;x+=1;consty:i16=@round(x);tryexpect(y==std.math.maxInt(i16));}test"@floor to integer"{varx:f32=300.4;x+=1;consty:u8=@floor(x);tryexpect(y==255);}test"@floor to integer in bounds"{varx:f32=-100.4;x+=1;consty:i8=@floor(x);tryexpect(y==-101);}test"@ceil to integer"{varx:f32=300.4;x+=1;consty:u8=@ceil(x);tryexpect(y==255);}test"@ceil to integer in bounds"{varx:f32=-100.4;x+=1;consty:i8=@ceil(x);tryexpect(y==-100);}test"@trunc to integer"{varx:f32=300.4;x+=1;consty:u8=@trunc(x);tryexpect(y==255);}test"@trunc to integer in bounds"{varx:f32=-100.4;x+=1;consty:i8=@trunc(x);tryexpect(y==-100);}test"@round to integer"{varx:f32=300.4;x+=1;consty:u8=@round(x);tryexpect(y==255);}test"@round to integer in bounds"{varx:f32=-100.4;x+=1;consty:i8=@round(x);tryexpect(y==-100);}
Uses of std.math.lossyCast can be upgraded to @round.
Related:
Migrated from https://github.com/ziglang/zig/issues/19575
Original proposal below. It was accepted, discussed, and then un-accepted for re-evaluation, and is now open for comments and counter proposals.
A pull request partially implementing some of this will be landing imminently: https://codeberg.org/ziglang/zig/pulls/30906
----
## Motivation
* Make using floating point values in game development less cumbersome. It should be low friction to convert a float to an int. A chained builtin is too much friction.
* Make the points where illegal behavior can possibly occur obvious.
* Delete `std.math.lossyCast`. This functionality should be provided by the language.
## Updated Builtins
These four functions have clarified definitions:
* `@round`: Returns the nearest representable integer to the operand; away from zero in halfway case.
* `@trunc`: Returns the nearest representable integer to the operand, towards zero.
* `@floor`: Returns the nearest representable integer to the operand, towards negative infinity.
* `@ceil`: Returns the nearest representable integer to the operand, towards positive infinity.
When the result type is non-integer, the behavior is the same as before. If the operand is nan or infinite, the operand is returned. In this case an integer operand is not allowed except where it would be allowed by `@as`.
When the result type is integer, these functions have a saturating effect. A value outside the integer range is clamped; nan is illegal. In this case an integer operand is allowed.
## Conformance Tests
```zig
const std = @import("std");
const expect = std.testing.expect;
test "@round on integers" {
var x: i32 = 1 << 18;
x += 1;
const y: i16 = @round(x);
try expect(y == std.math.maxInt(i16));
}
test "@floor to integer" {
var x: f32 = 300.4;
x += 1;
const y: u8 = @floor(x);
try expect(y == 255);
}
test "@floor to integer in bounds" {
var x: f32 = -100.4;
x += 1;
const y: i8 = @floor(x);
try expect(y == -101);
}
test "@ceil to integer" {
var x: f32 = 300.4;
x += 1;
const y: u8 = @ceil(x);
try expect(y == 255);
}
test "@ceil to integer in bounds" {
var x: f32 = -100.4;
x += 1;
const y: i8 = @ceil(x);
try expect(y == -100);
}
test "@trunc to integer" {
var x: f32 = 300.4;
x += 1;
const y: u8 = @trunc(x);
try expect(y == 255);
}
test "@trunc to integer in bounds" {
var x: f32 = -100.4;
x += 1;
const y: i8 = @trunc(x);
try expect(y == -100);
}
test "@round to integer" {
var x: f32 = 300.4;
x += 1;
const y: u8 = @round(x);
try expect(y == 255);
}
test "@round to integer in bounds" {
var x: f32 = -100.4;
x += 1;
const y: i8 = @round(x);
try expect(y == -100);
}
```
Uses of `std.math.lossyCast` can be upgraded to `@round`.
Related:
* https://github.com/ziglang/zig/issues/3806
* https://github.com/ziglang/zig/issues/11234
* https://github.com/ziglang/zig/issues/13642