@floatToInt, @intToFloat gone

I have noticed my code that uses float ↔ int coercion builtins, @intToFloat and @floatToInt, does not compile with the latest Zig, 0.11.0-dev.3803+7ad104227.

I then opened the documentation, and found that these builtins are not there, but I have found two new ones:

@floatFromInt(int: anytype) anytype

Converts an integer to the closest floating point representation.
The return type is the inferred result type. To convert the other way, use @intFromFloat.
This cast is always safe. 

and

@intFromFloat(float: anytype) anytype

Converts the integer part of a floating point number to the inferred result type.

If the integer part of the floating point number cannot fit in the destination type,
it invokes safety-checked Undefined Behavior. 

These look more than just reskin of the original @intToFloat and @floatToInt, in that the desired type is not specified anymore.

Please forgive me if it was described already, but where do I read on how the type of the result is inferred? Is there a way to control this behavior?

1 Like

It’s inferred from the resulting type of the expression, like so:

const foo: usize = @intFromFloat(42.0); // usize inferred from the type we're initializing

var bar: [2]i32 = .{
   @intFromFloat(0.0), // i32 inferred from the type that we're initializing
   @intFromFloat(1.1),
};

fn baz(qux: u64) void { ... }

test {
   baz(@intFromFloat(10.0)); // inferred from the parameter type of `baz`
}
3 Likes

That was the only thing I could think of.
Thank you for the confirmation.

I must be seeing things, but @intFromFloat doesn’t compile for me if I try using it following the documentation and your example:

const screen_height = 450;
const scroll_speed = 4;
. . .
var ball_pos_y: c_int = screen_height / 2 - 40;
. . .
ball_pos_y -= @intFromFloat(@round(c.GetMouseWheelMove())) * scroll_speed;

I get this error

zig build-exe main.zig -O Debug  -idirafter ./ -lc -lraylib
main.zig:46:23: error: expected 2 arguments, found 1
        ball_pos_y -= @intFromFloat(@round(c.GetMouseWheelMove())) * scroll_speed;
                      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

But it does compile if I use it in the same manner as the old @floatToInt:

ball_pos_y -= @intFromFloat(c_int, @round(c.GetMouseWheelMove())) * scroll_speed;

zig version 0.11.0-dev.3803+7ad104227

Edit: but all the uses of @intFromFloat in Zig itself / stdlib are following the new pattern (1 argument): https://github.com/search?q=repo%3Aziglang%2Fzig%20%40intFromFloat&type=code
:dizzy_face:

You must have a version of zig master that doesn’t have yet that change merged in.
I’m also seeing that the website CI is red so I’ll need to fix it before you see the updated download link on the website.

So… was there a transitional version where @intFromFloat and @floatFromInt took target type as the first argument?
The version I’m using is from https://ziglang.org/download/, zig-linux-x86_64-0.11.0-dev.3803+7ad104227.tar.xz

Definitely. The website CI is probably red on that second change, I’ll fix it in today’s live stream.

1 Like

BTW, I’ve been replacing @floatToInt’s with @intFromFloat’s and @intToFloat’s with @floatFromInt’s and ran into one of my raylib examples causing a segfault in Zig 0.11.0-dev.3803+7ad104227:

$ ./build_example.sh 27

Building zig-raylib-27-core-custom-frame-control ..
zig build-exe main.zig -O Debug  -idirafter ./ -lc -lraylib
./build.sh: line 55:  7957 Segmentation fault      (core dumped) zig build-exe main.zig ${tmp_raylib_zig_build_mode} ${tmp_raylib_include_arg} -idirafter ./ ${tmp_raylib_lib_arg}

The code in question is this.

I just fixed the website, you can now find in the download section an updated build of the compiler, see how that one works for you.

Have you tried using zig fmt? It would automatically change the code.

Yep I did, for some cases I wanted to do the cleanup manually since zig fmt in this case will do something that work every time (add @as) but that sometimes can be avoided / made nicer by restructuring the code slightly.

That would break my formatting though, so not an option.
I’m a life-long Allman style fan / K&R style hater, and I’ll die defending this hill.

1 Like

So, just as I feared, the compiler can’t infer the target type in all situations.
I was hoping it would be smart enough to understand cases like

const scroll_speed = 4;
const screen_height = 450;
var ball_pos_y: c_int = screen_height / 2 - 40;
. . .
ball_pos_y -= @intFromFloat(@round(c.GetMouseWheelMove())) * scroll_speed;

But nope:

main.zig:46:23: error: @intFromFloat must have a known result type
        ball_pos_y -= @intFromFloat(@round(c.GetMouseWheelMove())) * scroll_speed;
                      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.zig:46:23: note: use @as to provide explicit result type

So now yet another builtin has to be used:

ball_pos_y -= @as(c_int, @intFromFloat(@round(c.GetMouseWheelMove()))) * scroll_speed;

Starting to look like Lisp.

Compare to the original line:

ball_pos_y -= @floatToInt(c_int, @round(c.GetMouseWheelMove())) * scroll_speed;

Having an “optional” (meaning non-mandatory) type parameter in @intFromFloat would be nicer, IMO. For example

@intFromFloat(_, 0.0)

And then, if the type can’t be inferred:

@intFromFloat(i32, 1.0)

Another example where I honestly don’t understand why the type can’t be inferred:

error: @intFromFloat must have a known result type
            if (@intFromFloat(position) >= c.GetScreenWidth())
                ^~~~~~~~~~~~~~~~~~~~~~~

This is funny:

// @intCast -> usize
for (0..@intCast(@divTrunc(c.GetScreenWidth(), 200))) |i|
    c.DrawRectangle(200 * i, 0, 1, c.GetScreenHeight(), c.SKYBLUE);

Compilation error:

main.zig:94:37: error: expected type 'c_int', found 'usize'
                c.DrawRectangle(200 * i, 0, 1, c.GetScreenHeight(), c.SKYBLUE);
                                ~~~~^~~
main.zig:94:37: note: signed 32-bit int cannot represent all possible unsigned 64-bit values                                                  
/home/archie/.cache/zig/o/b12e78392fed5667ebea9425d5f87703/cimport.zig:854:35: note: parameter type declared here
pub extern fn DrawRectangle(posX: c_int, posY: c_int, width: c_int, height: c_int, color: Color) void;
                                  ^~~~~

Okay, let’s add @intCast. Since DrawRectangle’s first parameter is known to be c_int, the type should be easy to infer, right?

c.DrawRectangle(200 * @intCast(i), 0, 1, c.GetScreenHeight(), c.SKYBLUE);

Wrong:

main.zig:94:39: error: @intCast must have a known result type
                c.DrawRectangle(200 * @intCast(i), 0, 1, c.GetScreenHeight(), c.SKYBLUE);
                                      ^~~~~~~~~~~
main.zig:94:39: note: use @as to provide explicit result type

Whatever, let’s add @as if that’s what it takes

c.DrawRectangle(200 * @as(c_int, @intCast(i)), 0, 1, c.GetScreenHeight(), c.SKYBLUE);

Wham!

$ zig build-exe main.zig -O Debug -lc -lraylib
Segmentation fault (core dumped)

Here’s the code.

Update: looks like the compiler segfault is not directly related to new builtins, but to this issue.

Yet another example that causes segfault in the compiler:

$ zig build-exe main.zig -lc -lraylib
Segmentation fault (core dumped)

Used to compile yesterday with 0.11.0-dev.3803+7ad104227 which had “half-baked” @floatFromInt (with explicit target type parameter) and old @ptrCast (also with explicit target type parameter). What I mean is I have modified yesterday’s code, and it kills the compiler today.

I’m not sure if this is some sort of a transient issue, like that thing with @floatFromInt which had explicit target type parameter. If not, I’ll open an issue at https://github.com/ziglang/zig/issues

Edit: one more example that used to compile yesterday (with 3803), but causes segfault in 3857. Yesterday’s code.

Looks like the bug that causes the compiler to crash is related to C variadic function call + @ptrCast situation.
Opened a Github issue.

Also, my code is suffering from this issue.

You could write it as it was recommended:

    const screen_height = 450;
    const scroll_speed = 4;
    var ball_pos_y: c_int = screen_height / 2 - 40;
    const mouse_wheel_move: c_int = @intFromFloat(@round(1.3));
    ball_pos_y -= mouse_wheel_move * scroll_speed;

Makes it clearer.

3 Likes

Yes, you can split your expressions into parts.

Yesterday I’ve spent hours adding @as(<type> to @intFromFloat and @floatFromInt’s.

Anything more complex than foo = @intFromFloat(bar) or bar = @floatFromInt(foo) and the compiler gets confused about types.

Literally anything, for example foo = @intFromFloat(bar) * 2 and you have to either wrap it in @as(<type>, ...), or split the expression.

Makes me wonder what was the point of this change.

My beginnings in programming were assembler programs. This way you get used to writing one execution per line quite fast. Writing statements nested in one line is something I encountered for the first time in C and I still don’t find it helpful because it makes the code hard to read. Of course it can be that you - when you develop your own code - say, not true, but a third person who wants to read your code still has the problems. And I think a programming language should support readability, because that makes it easier to use, especially in collaborative work.

1 Like

I was referring to the modification of many builtins, such as @intFromFloat, @floatFromInt, @ptrCast, @alignCast, etc. They now infer the desired type, instead of having you specify it directly.

So far so good, but this mechanism fails in so many cases, necessitating user intervention in the form of @as, and this is why I was wondering if the change was worth it.