# `@bitCast`

### `@bitCast`

`@bitCast` reinterprets the bits of one value as another type.

It does not do a normal conversion.

It does not change the bits.

It takes the same raw bit pattern and gives it a new type.

#### A Simple Example

Suppose you have a `u32`:

```zig
const x: u32 = 0x3f800000;
```

Those 32 bits can also be interpreted as an `f32`.

```zig
const y: f32 = @bitCast(x);
```

The value of `y` is `1.0`, because the bit pattern `0x3f800000` is the IEEE 754 representation of `1.0` as a 32-bit float.

The integer value was not numerically converted.

This is not the same as:

```zig
const y: f32 = @floatFromInt(x);
```

That would convert the number `1065353216` into a floating-point value.

`@bitCast` keeps the bits exactly the same.

#### Source and Destination Must Have the Same Size

`@bitCast` only works when the source type and destination type have the same size.

This is valid:

```zig
const x: u32 = 0x3f800000;
const y: f32 = @bitCast(x);
```

Both `u32` and `f32` are 4 bytes.

This is invalid:

```zig
const x: u32 = 123;
const y: u64 = @bitCast(x);
```

A `u32` is 4 bytes. A `u64` is 8 bytes. Zig rejects this because the bit counts do not match.

If you want a numeric conversion, use a numeric cast or conversion builtin, not `@bitCast`.

#### `@bitCast` Is About Representation

A value has two views:

```text
meaning: what the value represents
bits:    how the value is stored
```

For example:

```zig
const n: u32 = 65;
```

The meaning is the number `65`.

The bits are the binary representation of `65`.

With normal conversion, Zig keeps the meaning.

With `@bitCast`, Zig keeps the bits.

That is the central difference.

#### Normal Conversion vs Bit Cast

Normal conversion:

```zig
const a: u8 = 65;
const b: u32 = a;
```

The number is still `65`. The destination type has more space, so the value is widened safely.

Bit cast:

```zig
const a: [4]u8 = .{ 65, 0, 0, 0 };
const b: u32 = @bitCast(a);
```

Here, Zig takes the four bytes and treats them as a `u32`.

The result depends on byte order and layout rules.

For beginner code, remember this: `@bitCast` works at the storage level, not the ordinary arithmetic level.

#### A Complete Example

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

pub fn main() void {
    const bits: u32 = 0x3f800000;
    const value: f32 = @bitCast(bits);

    std.debug.print("{}\n", .{value});
}
```

Output:

```text
1e0
```

The exact formatting may look different depending on how the float is printed, but the value is `1.0`.

#### Bit Casting Structs

You can bit cast between types with the same size.

Example:

```zig
const Pair = extern struct {
    a: u16,
    b: u16,
};

const raw: u32 = 0x00020001;
const pair: Pair = @bitCast(raw);
```

This treats the 32-bit integer as a struct containing two 16-bit fields.

The exact field values depend on endianness. On little-endian systems, the lower byte comes first in memory.

This is one reason you must be careful when using `@bitCast` for file formats or network protocols. External formats often define a specific byte order. Your CPU may use a different byte order.

#### Use `extern` or `packed` When Layout Matters

If you bit cast into a struct, the struct layout matters.

A normal Zig struct does not promise the same layout as a C struct or packed binary format.

For layout-sensitive code, use `extern struct` or `packed struct` when appropriate.

Example:

```zig
const Header = extern struct {
    magic: u16,
    version: u16,
};
```

An `extern struct` follows C ABI layout rules.

For exact bit-level layout, use `packed struct`:

```zig
const Flags = packed struct {
    read: bool,
    write: bool,
    execute: bool,
};
```

Do not bit cast into ordinary structs when the byte layout must be stable across targets.

#### `@bitCast` Creates a New Value

`@bitCast` works on values.

It does not create another pointer to the same memory.

Example:

```zig
const x: u32 = 0x3f800000;
const y: f32 = @bitCast(x);
```

`y` is a new value whose bits match `x`.

This is different from `@ptrCast`, which changes how a pointer views existing memory.

```zig
const p: *u32 = &x;
const q: *f32 = @ptrCast(p);
```

Here, `q` points to the same memory as `p`, but with a different pointer type.

Use `@bitCast` when you want a value-level reinterpretation.

Use `@ptrCast` when you need pointer-level reinterpretation.

#### Common Use Cases

`@bitCast` appears in low-level code.

Common uses include:

```text
floating-point bit inspection
binary protocols
file format parsing
hashing
serialization
emulators
compilers
graphics code
SIMD-related code
hardware-oriented code
```

For normal application code, you rarely need it.

Most type changes should be normal conversions, not bit casts.

#### Example: Inspecting Float Bits

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

pub fn main() void {
    const value: f32 = 1.0;
    const bits: u32 = @bitCast(value);

    std.debug.print("0x{x}\n", .{bits});
}
```

Possible output:

```text
0x3f800000
```

This is useful when you need to inspect the internal representation of a floating-point number.

#### Example: Reading Four Bytes as an Integer

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

pub fn main() void {
    const bytes = [4]u8{ 1, 0, 0, 0 };
    const value: u32 = @bitCast(bytes);

    std.debug.print("{}\n", .{value});
}
```

On a little-endian target, this prints:

```text
1
```

But this is not portable as a file format parser unless the byte order is handled deliberately.

For external binary data, prefer explicit endian-aware functions from the standard library.

The clearer idea is:

```text
File bytes have a defined byte order.
Your CPU has its own byte order.
Do not confuse the two.
```

#### When Not to Use `@bitCast`

Do not use `@bitCast` for ordinary numeric conversion.

Wrong idea:

```zig
const x: u32 = 42;
const y: f32 = @bitCast(x);
```

This does not produce `42.0`.

It produces a floating-point value whose bits are the same as integer `42`. That value is tiny and meaningless for normal arithmetic.

Use numeric conversion instead.

For integer to float:

```zig
const y: f32 = @floatFromInt(x);
```

For float to integer:

```zig
const n: u32 = @intFromFloat(y);
```

For integer size changes:

```zig
const small: u8 = @intCast(big);
```

#### Compile-Time Checking

Zig checks that the source and destination sizes match.

Example:

```zig
const a: u16 = 10;
const b: u32 = @bitCast(a);
```

This is rejected because 2 bytes cannot be bit cast into 4 bytes.

This check is valuable. It prevents many representation mistakes before the program runs.

#### How to Read `@bitCast`

When you see:

```zig
const b: B = @bitCast(a);
```

read it as:

```text
Create a B value using the same bits as a.
```

Then ask:

```text
Do A and B have the same size?
Is the destination type valid for these bits?
Does layout matter?
Does endianness matter?
Would a normal conversion be more correct?
```

Most bugs with `@bitCast` come from using it when the programmer really wanted a normal conversion.

#### Key Idea

`@bitCast` reinterprets a value’s bits as another type of the same size.

It preserves representation, not meaning.

Use it for low-level representation work. Avoid it for ordinary conversions.

