From e5d0d53e11bd4e6a3a9872bf8066f23569aa89a7 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Sun, 29 Sep 2019 19:06:30 +0200 Subject: [PATCH 1/3] Add examples to clarify the casting rules --- src/expressions/operator-expr.md | 82 ++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/src/expressions/operator-expr.md b/src/expressions/operator-expr.md index a9e91f0a6..7b2f5f4db 100644 --- a/src/expressions/operator-expr.md +++ b/src/expressions/operator-expr.md @@ -398,25 +398,88 @@ reference types and `mut` or `const` in pointer types. * 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) + + ```rust + # #![allow(overflowing_literals)] + assert_eq!(42i8 as u8, 42u8); + assert_eq!(-1i8 as u8, 255u8); + assert_eq!(255u8 as i8, -1i8); + assert_eq!(0xffu8 as i8, 0xffi8); + assert_eq!(-1i16 as u16, 65535u16); + ``` + * Casting from a larger integer to a smaller integer (e.g. u32 -> u8) will truncate + + ```rust + # #![allow(overflowing_literals)] + assert_eq!(42u16 as u8, 42u8); + assert_eq!(1234u16 as u8, 210u8); + assert_eq!(0xabcdu16 as u8, 0xcdu8); + + assert_eq!(-42i16 as i8, -42i8); + assert_eq!(1234u16 as i8, -46i8); + assert_eq!(0xabcdi16 as i8, 0xcdi8); + ``` + * 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 + + ```rust + # #![allow(overflowing_literals)] + assert_eq!(42i8 as i16, 42i16); + assert_eq!(-17i8 as i16, -17i16); + assert_eq!(0b1000_1010u8 as u16, 0b0000_0000_1000_1010u16, "Zero-extend"); + assert_eq!(0b0000_1010i8 as i16, 0b0000_0000_0000_1010i16, "Sign-extend 0"); + assert_eq!(0b1000_1010i8 as i16, 0b1111_1111_1000_1010i16, "Sign-extend 1"); + ``` + * 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. + + ```rust + assert_eq!(42.9f32 as i32, 42); + assert_eq!(-42.9f32 as i32, -42); + assert_eq!(42_000_000f32 as i32, 42_000_000); + println!("Undefined Behavior: {}", 1_000_000_000_000_000f32 as i32); + println!("Undefined Behavior: {}", std::f32::NEG_INFINITY as i32); + println!("Undefined Behavior: {}", std::f32::NAN as i32); + ``` + * 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)` + + ```rust + assert_eq!(1337i32 as f32, 1337f32); + assert_eq!(123_456_789i32 as f32, 123_456_790f32, "Rounded"); + assert_eq!(0xffffffff_ffffffff_ffffffff_ffffffff_u128 as f32, std::f32::INFINITY); + ``` + * Casting from an f32 to an f64 is perfect and lossless + + ```rust + assert_eq!(1_234.5f32 as f64, 1_234.5f64); + assert_eq!(std::f32::INFINITY as f64, std::f64::INFINITY); + assert!((std::f32::NAN as f64).is_nan()); + ``` + * 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 + ```rust + assert_eq!(1_234.5f64 as f32, 1_234.5f32); + assert_eq!(1_234_567_891.123f64 as f32, 1_234_567_890f32, "Rounded"); + assert_eq!(std::f64::INFINITY as f32, std::f32::INFINITY); + assert!((std::f64::NAN as f32).is_nan()); + ``` + \* 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. @@ -437,15 +500,34 @@ Casting is limited to the following kinds of enumerations: * [Unit-only enums] * [Field-less enums] without [explicit discriminants], or where only unit-variants have explicit discriminants +```rust +enum Enum { A, B, C } +assert_eq!(Enum::A as i32, 0); +assert_eq!(Enum::B as i32, 1); +assert_eq!(Enum::C as i32, 2); +``` + #### Primitive to integer cast * `false` casts to `0`, `true` casts to `1` * `char` casts to the value of the code point, then uses a numeric cast if needed. +```rust +assert_eq!(false as i32, 0); +assert_eq!(true as i32, 1); +assert_eq!('A' as i32, 65); +assert_eq!('Ö' as i32, 214); +``` + #### `u8` to `char` cast Casts to the `char` with the corresponding code point. +```rust +assert_eq!(65u8 as char, 'A'); +assert_eq!(214u8 as char, 'Ö'); +``` + #### Pointer to address cast Casting from a raw pointer to an integer produces the machine address of the referenced memory. From 4226cece14a9d38f6c1387ab60b5078c440f1e21 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Wed, 13 Nov 2024 19:51:30 -0800 Subject: [PATCH 2/3] Update example for changes in float behavior --- src/expressions/operator-expr.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/expressions/operator-expr.md b/src/expressions/operator-expr.md index 7b2f5f4db..436cf41e6 100644 --- a/src/expressions/operator-expr.md +++ b/src/expressions/operator-expr.md @@ -444,9 +444,9 @@ reference types and `mut` or `const` in pointer types. assert_eq!(42.9f32 as i32, 42); assert_eq!(-42.9f32 as i32, -42); assert_eq!(42_000_000f32 as i32, 42_000_000); - println!("Undefined Behavior: {}", 1_000_000_000_000_000f32 as i32); - println!("Undefined Behavior: {}", std::f32::NEG_INFINITY as i32); - println!("Undefined Behavior: {}", std::f32::NAN as i32); + assert_eq!(std::f32::NAN as i32, 0); + assert_eq!(1_000_000_000_000_000f32 as i32, 0x7fffffffi32); + assert_eq!(std::f32::NEG_INFINITY as i32, -0x80000000i32); ``` * Casting from an integer to float will produce the closest possible float \* From bb6f41bd39a2e3cead6cf9a5dab67ae160d6326c Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Wed, 13 Nov 2024 19:56:08 -0800 Subject: [PATCH 3/3] Avoid the use of overflowing_literals in the examples I think that it could have some potential confusion since it isn't displayed in the text that these are overflowing, and it requires an extra layer of reasoning needed to understand how it translates. --- src/expressions/operator-expr.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/expressions/operator-expr.md b/src/expressions/operator-expr.md index 436cf41e6..530d41d10 100644 --- a/src/expressions/operator-expr.md +++ b/src/expressions/operator-expr.md @@ -400,11 +400,9 @@ reference types and `mut` or `const` in pointer types. (Rust uses 2's complement for negative values of fixed integers) ```rust - # #![allow(overflowing_literals)] assert_eq!(42i8 as u8, 42u8); assert_eq!(-1i8 as u8, 255u8); assert_eq!(255u8 as i8, -1i8); - assert_eq!(0xffu8 as i8, 0xffi8); assert_eq!(-1i16 as u16, 65535u16); ``` @@ -412,14 +410,13 @@ reference types and `mut` or `const` in pointer types. truncate ```rust - # #![allow(overflowing_literals)] assert_eq!(42u16 as u8, 42u8); assert_eq!(1234u16 as u8, 210u8); assert_eq!(0xabcdu16 as u8, 0xcdu8); assert_eq!(-42i16 as i8, -42i8); assert_eq!(1234u16 as i8, -46i8); - assert_eq!(0xabcdi16 as i8, 0xcdi8); + assert_eq!(0xabcdi32 as i8, -51i8); ``` * Casting from a smaller integer to a larger integer (e.g. u8 -> u32) will @@ -427,12 +424,11 @@ reference types and `mut` or `const` in pointer types. * sign-extend if the source is signed ```rust - # #![allow(overflowing_literals)] assert_eq!(42i8 as i16, 42i16); assert_eq!(-17i8 as i16, -17i16); assert_eq!(0b1000_1010u8 as u16, 0b0000_0000_1000_1010u16, "Zero-extend"); assert_eq!(0b0000_1010i8 as i16, 0b0000_0000_0000_1010i16, "Sign-extend 0"); - assert_eq!(0b1000_1010i8 as i16, 0b1111_1111_1000_1010i16, "Sign-extend 1"); + assert_eq!(0b1000_1010u8 as i8 as i16, 0b1111_1111_1000_1010u16 as i16, "Sign-extend 1"); ``` * Casting from a float to an integer will round the float towards zero