Is there a more idiomatic way to express this encoding ?

```
fn foo(x: i8) -> u8 {
((x as i16) + 128) as u8
}
fn foo2(x: i16) -> u16 {
((x as i32) + (1 << 15)) as u16; // EDIT: typo, was 1<<16; should be 1<<15
}
```

Is there a more idiomatic way to express this encoding ?

```
fn foo(x: i8) -> u8 {
((x as i16) + 128) as u8
}
fn foo2(x: i16) -> u16 {
((x as i32) + (1 << 15)) as u16; // EDIT: typo, was 1<<16; should be 1<<15
}
```

the first one flips the highest bit, right? what does the second one do?

1 Like

The second one is a typo, which is now fixed. Was 1<<16, is now 1<<15.

These are equivalent:

```
fn foo(x: i8) -> u8 {
(x as u8).wrapping_add(128)
}
fn foo2(x: i16) -> u16 {
(x as u16).wrapping_add(1 << 15)
}
```

Sorry, where is how (x as u8) documented? I thought (perhaps incorrectly) it rounded all neg to 0.

## Numeric cast

Casting between two integers of the same size (e.g. i32 -> u32) is a no-op (Rust uses 2's complement for negative values of fixed integers)

Casting from a larger integer to a smaller integer (e.g. u32 -> u8) will truncate

Casting from a smaller integer to a larger integer (e.g. u8 -> u32) will

- zero-extend if the source is unsigned
- sign-extend if the source is signed
Casting from a float to an integer will round the float towards zero

`NaN`

will return`0`

- Values larger than the maximum integer value, including
`INFINITY`

, will saturate to the maximum value of the integer type.- Values smaller than the minimum integer value, including
`NEG_INFINITY`

, will saturate to the minimum value of the integer type.Casting from an integer to float will produce the closest possible float *

- if necessary, rounding is according to
`roundTiesToEven`

mode ***- on overflow, infinity (of the same sign as the input) is produced
- note: with the current set of numeric types, overflow can only happen on
`u128 as f32`

for values greater or equal to`f32::MAX + (0.5 ULP)`

Casting from an f32 to an f64 is perfect and lossless

Casting from an f64 to an f32 will produce the closest possible f32 **

- if necessary, rounding is according to
`roundTiesToEven`

mode ***- on overflow, infinity (of the same sign as the input) is produced
if integer-to-float casts with this rounding mode and overflow behavior are not supported natively by the hardware, these casts will likely be slower than expected.

** if f64-to-f32 casts with this rounding mode and overflow behavior are not supported natively by the hardware, these casts will likely be slower than expected.

*** as defined in IEEE 754-2008 Â§4.3.1: pick the nearest floating point number, preferring the one with an even least significant digit if exactly halfway between two floating point numbers.

2 Likes

Casts between signed and unsigned integers of the same size are equivalent to a transmute and leaves the bit pattern unchanged.

1 Like

```
fn foo(x: i8) -> u8 {
x.wrapping_sub(i8::MIN) as u8
}
fn foo2(x: i16) -> u16 {
x.wrapping_sub(i16::MIN) as u16
}
```

6 Likes

Always good to see how LLVM doesnâ€™t care how you implement stuff, anyways:

```
pub fn foo(x: i8) -> u8 {
((x as i16) + 128) as u8
}
pub fn foo1(x: i8) -> u8 {
x.wrapping_sub(i8::MIN) as u8
}
pub fn foo2(x: i8) -> u8 {
(x as u8) ^ (1 << (u8::BITS - 1))
}
```

```
playground::foo:
leal -128(%rdi), %eax
retq
.set playground::foo1, playground::foo
.set playground::foo2, playground::foo
```

(do compile to â€śASMâ€ť in the playground in â€śReleaseâ€ť mode)

(all 3 get the same assembly, and then deduplicated)

5 Likes

However, I was surprised that LLVM wasn't able to optimize this:

```
fn foo(x: i32) -> u32 {
x.wrapping_sub(i32::MIN) as u32
}
pub fn subtract(a: i32, b: i32) -> u32 {
foo(a) - foo(b)
}
```

`subtract`

is equivalent to `a - b`

(in release mode) but LLVM doesn't see it.

1 Like