# Bits monstrosity

**URL:** <https://ziggit.dev/t/bits-monstrosity/6727>\
**Category:** Help\
**Tags:** language\
**Created:** [November 6, 2024, 10:34pm UTC](https://ziggit.dev/t/bits-monstrosity/6727 "2024-11-06T22:34:58Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![ericlang](https://ziggit.dev/user_avatar/ziggit.dev/ericlang/32/8515_2.png) [@ericlang](https://ziggit.dev/u/ericlang)\
**Post date:** [November 6, 2024, 10:34pm UTC](https://ziggit.dev/t/bits-monstrosity/6727/1 "2024-11-06T22:34:58Z")

</div>

```zig
const mask: u8 = @intCast(@as(u8, 1) << @intCast(bit));

```

Do we really have to write stuff this way? instead of:

```zig
const mask: u8 = 1 << bit;

```

---

<div class="post-metadata">

**Author:** ![dimdin](https://ziggit.dev/user_avatar/ziggit.dev/dimdin/32/1457_2.png) [@dimdin](https://ziggit.dev/u/dimdin)\
**Post date:** [November 6, 2024, 10:38pm UTC](https://ziggit.dev/t/bits-monstrosity/6727/2 "2024-11-06T22:38:11Z")

</div>

This works, and prints `16`:

```zig
const std = @import("std");

pub fn main() void {
    const bit: u8 = 4;
    const mask: u8 = 1 << bit;
    std.debug.print("{d}\n", .{mask});
}

```

What is the type of `bit` in your case?

---

<div class="post-metadata">

**Author:** ![ericlang](https://ziggit.dev/user_avatar/ziggit.dev/ericlang/32/8515_2.png) [@ericlang](https://ziggit.dev/u/ericlang)\
**Post date:** [November 7, 2024, 8:17am UTC](https://ziggit.dev/t/bits-monstrosity/6727/4 "2024-11-07T08:17:40Z")

</div>

It was more of a general question ragarding numbercrunching.  
Also when using for() loops casting is needed,.

```zig
const width: u32;
for(0..width) |i|
{
    // and now the `i` has magically become a usize
}

```

I guess it is party inexperience. But still it sometimes makes code less readable.

---

<div class="post-metadata">

**Author:** ![LucasSantos91](https://ziggit.dev/user_avatar/ziggit.dev/lucassantos91/32/440_2.png) [@LucasSantos91](https://ziggit.dev/u/LucasSantos91)\
**Post date:** [November 7, 2024, 10:59am UTC](https://ziggit.dev/t/bits-monstrosity/6727/5 "2024-11-07T10:59:55Z")

</div>

> [@ericlang](#):
>
> Also when using for() loops casting is needed

[Related](https://github.com/ziglang/zig/issues/14704).  
I am curious as to what made Andrew reject this.

---

<div class="post-metadata">

**Author:** ![ericlang](https://ziggit.dev/user_avatar/ziggit.dev/ericlang/32/8515_2.png) [@ericlang](https://ziggit.dev/u/ericlang)\
**Post date:** [November 7, 2024, 11:49am UTC](https://ziggit.dev/t/bits-monstrosity/6727/6 "2024-11-07T11:49:37Z")

</div>

Well I guess because there are other issues. First the bugs than maybe some language additions.  
Looping should definitely be possible with all int types, also negative ones.  
Maybe even with inclusive numbers (using `...`).

---

<div class="post-metadata">

**Author:** ![Sze](https://ziggit.dev/user_avatar/ziggit.dev/sze/32/496_2.png) [@Sze](https://ziggit.dev/u/Sze)\
**Post date:** [November 7, 2024, 1:34pm UTC](https://ziggit.dev/t/bits-monstrosity/6727/7 "2024-11-07T13:34:22Z")

</div>

This is a duplicate of the issue @LucasSantos91 mentioned (and it is also closed):

> <https://github.com/ziglang/zig/issues/17039#issuecomment-2292937639>
>
> \### Zig Version
> 
> 0.11.0
> 
> \### Steps to Reproduce and Observed Behavior
> 
> I h…ave this function:
> 
> \`\`\`zig
> pub fn sprite(data: \*const Sprite, x: u8, y: u8) void {
> std.debug.assert(x \<= 127);
> std.debug.assert(y \<= 127);
>     
> for (data, 0..8) |column, sy| {
> for (column, 0..8) |color, sx| {
> if (x + sx \> 127 or y + sy \> 127) break;
> const sxc: u8 = @intCast(sx);
> const syc: u8 = @intCast(sy);
> if (color != transparency) pixel(sxc + x, syc + y, color);
> }
> }
> }
> \`\`\`
> where Sprite is \`\[8\]\[8\]Color\`
> 
> (Ignore that the function has bugs, that's not really the point 🙂)
> 
> Doesn't matter if I use \`0..\` or \`0..8\` it still doesn't compile, as the range is seemingly inferred to be usize.
> 
> The only way to make it work is to use the two casts as shown in the function above to make the compiler happy.
> This is annoying because this range is compile time known, so it should be clear it can fit in a \`u8\`.
> 
> \### Expected Behavior
> 
> I expected to be able to use \`u8\` in loops.

However Andrew mentions in the linked comment above:

> there is a chance you may get your wish with [#3806](https://github.com/ziglang/zig/issues/3806).

Which is this issue:

> <https://github.com/ziglang/zig/issues/3806#issuecomment-2365301602>
>
> Zig already has ranged integer types, however the range is required to be a sign…ed or unsigned power of 2. This proposal is for generalizing them further, to allow any arbitrary range.
> 
> \`\`\`zig
> comptime {
> assert(i32 == @Int(-1 \<\< 31, 1 \<\< 31));
> assert(u32 == @Int(0, 1 \<\< 32));
> assert(u0 == @Int(0, 1);
> assert(noreturn == @int(0, 0));
> }
> \`\`\`
> 
> \----
> 
> Let's consider some reasons to do this:
> 
> One common practice for C developers is to use \`-1\` or \`MAX\_UINT32\` (and related) constants as an \*in-bound\* indicator of metadata. For example, the stage1 compiler uses a \`size\_t\` field to indicate the ABI size of a type, but the value \`SIZE\_MAX\` is used to indicate that the size is not yet computed.
> 
> In Zig we want people to use \[Optionals\](https://ziglang.org/documentation/master/#Optionals) for this, but there's a catch: the in-bound special value uses less memory for the type. In Zig on 64-bit targets, \`@sizeOf(usize) == 8\` and \`@sizeOf(?usize) == 16\`. That's a huge cost to pay, for something that could take up 0 bits of information if you are willing to give up a single value inside the range of a \`usize\`.
> 
> With ranged integers, this could be made type-safe:
> 
> \`\`\`zig
> const AbiSize = @Int(0, (1 \<\< usize.bit\_count) - 1);
> const MyType = struct {
> abi\_size: ?AbiSize,
> };
> var my\_type: MyType = undefined;
> 
> test "catching a bug" {
> var other\_thing: usize = 1234;
> 
> my\_type.abi\_size = other\_thing; // error: expected @Int(0, 18446744073709551615), found usize
> }
> \`\`\`
> 
> Now, not only do we have the Optionals feature of zig protecting against accidentally using a very large integer when it is supposed to indicate \`null\`, but we also have the compile error helping out with range checks. One can choose to deal with the larger ranged value by handling the possibility, and returning an error, or with \`@intCast\`, which inserts a handy safety check.
> 
> How about if there are 2 special values rather than 1?
> 
> \`\`\`zig
> const N = union(enum) {
> special1,
> special2,
> normal: @Int(0, (1 \<\< u32) - 2),
> };
> \`\`\`
> 
> Here, size of N would be 4 bytes.
> 
> \----
> 
> Let's consider another example, with enums.
> 
> Enums allow defining a set of possible values for a type:
> 
> \`\`\`zig
> const E = enum {
> one,
> two,
> three,
> };
> \`\`\`
> 
> There are 3 possible values of this type, so Zig chooses to use \`u2\` for the tag type. It will require 1 byte to represent it, wasting 6 bits. If you wrap it in an optional, that will be 16 bits to represent something that, according to information theory, requires only 2 bits. And Zig's hands are tied; because currently each field requires ABI alignment, each byte is necessary.
> 
> If #3802 is accepted and implemented, and the \`is\_null\` bit of optionals becomes \`align(0)\`, then \`?E\` can remain 1 byte, and \`?E\` in a struct with \`align(0)\` will take up 3 bits.
> 
> However, consider if the enum was allowed to choose a ranged integer type. It would choose \`@Int(0, 3)\`. Wrapped in an optional, it actually could choose to use the integer value 3 as the \`is\_null\` bit. Then \`?E\` in a struct will take up 2 bits.
> 
> Again, assuming #3802 is implemented, Zig would even be able to "flatten" several enums into the same integer:
> 
> \`\`\`zig
> const Mode = enum { // 2-bit tag type
> Debug,
> ReleaseSafe,
> ReleaseFast,
> ReleaseSmall
> };
> const Endian = enum { // 1-bit tag type
> big,
> little,
> };
> pub const AtomicOrder = enum { // 3-bit tag type
> Unordered,
> Monotonic,
> Acquire,
> Release,
> AcqRel,
> SeqCst,
> };
> pub const AtomicRmwOp = enum { // 4-bit tag type
> Xchg,
> Add,
> Sub,
> And,
> Nand,
> Or,
> Xor,
> Max,
> Min,
> };
> const MyFancyType = struct {
> mode: Mode align(0),
> endian: Endian align(0),
> atomic\_order: AtomicOrder align(0),
> op: AtomicRmwOp align(0),
> };
> \`\`\`
> 
> If you add up all the bits of the tag type, it comes out to 10, meaning that the size of MyFancyType would have to be 2 bytes. However, with ranged integers as tag types, zig would be able to flatten out all the enum tag values into one byte. In fact there are only 21 total tag types here, leaving room for 235 more total tags before MyFancyType would have to gain another byte of size.
> 
> \----
> 
> This proposal would solve #747. Peer type resolution of comptime ints would produce a ranged integer:
> 
> \`\`\`zig
> export fn foo(b: bool) void {
> // master branch: error: cannot store runtime value in type 'comptime\_int'
> const x = if (b) -10 else 100;
> // proposal: @typeOf(x) == @Int(-10, 101)
> }
> \`\`\`
> 
> \----
> 
> With optional pointers, Zig has an optimization to use the zero address as the null value. The \`allowzero\` property can be used to indicate that the address 0 is valid. This is effectively treating the address as a ranged integer type! This optimization for optional pointers could now be described in userland types:
> 
> \`\`\`zig
> const PointerAddress = @Int(1, 1 \<\< usize.bit\_count);
> const Pointer = ?PointerAddress;
> 
> comptime {
> assert(@sizeOf(PointerAddress) == @sizeOf(usize));
> assert(@sizeOf(Pointer) == @sizeOf(usize));
> }
> \`\`\`
> 
> One possible extension to this proposal would be to allow pointer types to override the address integer type. Rather than \`allowzero\` which is single purpose, they could do something like this:
> 
> \`\`\`zig
> comptime {
> assert(\*addrtype(usize) i32 == \*allowzero i32);
> assert(\*addrtype(@Int(1, 1 \<\< usize.bit\_count)) == \*i32);
> }
> \`\`\`
> 
> This would also introduce type-safety to using more than just 0x0 as a special pointer address, which is perfectly acceptable on most hosted operating systems, and also typically set up in freestanding environments as well. Typically, the entire first page of memory is unmapped, and often the virtual address space is limited to 48 bits making \`@Int(os.page\_size, 1 \<\< 48)\` a good default address type for pointers on many targets! Combining this with the fact that pointers also have alignment bits to play with, this would give Zig's type system the ability to pack a lot of data into pointers which are annotated with \`align(0)\`.
> 
> \----
> 
> What about two's complement wrapping math operations? Two's complement only works on powers-of-two integer types. Wrapping math operations would not be allowed on non-power-of-two integer types. Compile error.

where @mnemnion linked to that comment.

So if we get integer types which can be any range, we might get for loops with arbitrary types too.

I think that would be pretty cool, but those integer types of any range might be a lot of work to implement, so I think we need to have some patience for that.

---

<div class="post-metadata">

**Author:** ![ericlang](https://ziggit.dev/user_avatar/ziggit.dev/ericlang/32/8515_2.png) [@ericlang](https://ziggit.dev/u/ericlang)\
**Post date:** [November 7, 2024, 1:56pm UTC](https://ziggit.dev/t/bits-monstrosity/6727/8 "2024-11-07T13:56:52Z")

</div>

Yes I saw some of the links. Except the last one.  
I think you are right it will be quite some work to get these things right.

(edit) like these things, which in C# creates a neverending loop as I noted by personal accident.

```zig
byte b = 10;
while (b >= 0)
{
        b--;
}

```

---

<div class="post-metadata">

**Author:** ![mnemnion](https://ziggit.dev/user_avatar/ziggit.dev/mnemnion/32/2478_2.png) [@mnemnion](https://ziggit.dev/u/mnemnion)\
**Post date:** [November 8, 2024, 5:54pm UTC](https://ziggit.dev/t/bits-monstrosity/6727/9 "2024-11-08T17:54:06Z")

</div>

> [@ericlang](#):
>
> Do we really have to write stuff this way?

In short: yes. Zig [peer type coercion](https://ziglang.org/documentation/master/#Type-Coercion) has a very simple rule for numeric types:

> [Integers](https://ziglang.org/documentation/master/#Integers) coerce to integer types which can represent every value of the old type, and likewise [Floats](https://ziglang.org/documentation/master/#Floats) coerce to float types which can represent every value of the old type.

Shifting has an additional rule: the shiftand must be of a width log₂ of the shifted type, so to shift a `u64` the shiftand must be a `u6` or smaller. A consequence of this rule is that a `comptime_int` can’t just be cast to the result type.

These rules can be annoying, but they solve more problems than they cause. I often add this function to code:

```zig
pub fn cast(N: type, val: anytype) N {
     return @intCast(val);
}

```

So I can write `cast(u8, something)` instead of `@as(u8, @intCast(something))`, this makes the resulting code easier to read and understand.
