# Conv i8 -> u8, i16 -> u16

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 {
}

fn foo2(x: i16) -> u16 {
}
``````

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