|
|
@ -4,13 +4,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
计算机和数值关联在一起的时间,远比我们想象的要长,因此数值类型可以说是有计算机以来就有的类型,下面内容将深入讨论 Rust 的数值类型以及相关的运算符。
|
|
|
|
计算机和数值关联在一起的时间,远比我们想象的要长,因此数值类型可以说是有计算机以来就有的类型,下面内容将深入讨论 Rust 的数值类型以及相关的运算符。
|
|
|
|
|
|
|
|
|
|
|
|
## 整数和浮点数
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Rust 使用一个相对传统的语法来创建整数(`1`,`2`,...)和浮点数(`1.0`,`1.1`,...)。整数、浮点数的运算和你在其它语言上见过的一致,都是通过常见的运算符来完成。
|
|
|
|
Rust 使用一个相对传统的语法来创建整数(`1`,`2`,...)和浮点数(`1.0`,`1.1`,...)。整数、浮点数的运算和你在其它语言上见过的一致,都是通过常见的运算符来完成。
|
|
|
|
|
|
|
|
|
|
|
|
> 不仅仅是数值类型,Rust 也允许在复杂类型上定义运算符,例如在自定义类型上定义 `+` 运算符,这种行为被称为运算符重载,Rust 具体支持的可重载运算符见[附录 B](https://course.rs/appendix/operators.html#运算符)
|
|
|
|
> 不仅仅是数值类型,Rust 也允许在复杂类型上定义运算符,例如在自定义类型上定义 `+` 运算符,这种行为被称为运算符重载,Rust 具体支持的可重载运算符见[附录 B](https://course.rs/appendix/operators.html#运算符)
|
|
|
|
|
|
|
|
|
|
|
|
#### 整数类型
|
|
|
|
## 整数类型
|
|
|
|
|
|
|
|
|
|
|
|
**整数**是没有小数部分的数字。之前使用过的 `i32` 类型,表示有符号的 32 位整数( `i` 是英文单词 _integer_ 的首字母,与之相反的是 `u`,代表无符号 `unsigned` 类型)。下表显示了 Rust 中的内置的整数类型:
|
|
|
|
**整数**是没有小数部分的数字。之前使用过的 `i32` 类型,表示有符号的 32 位整数( `i` 是英文单词 _integer_ 的首字母,与之相反的是 `u`,代表无符号 `unsigned` 类型)。下表显示了 Rust 中的内置的整数类型:
|
|
|
|
|
|
|
|
|
|
|
@ -42,91 +41,36 @@ Rust 使用一个相对传统的语法来创建整数(`1`,`2`,...)和浮
|
|
|
|
|
|
|
|
|
|
|
|
这么多类型,有没有一个简单的使用准则?答案是肯定的, Rust 整形默认使用 `i32`,例如 `let i = 1`,那 `i` 就是 `i32` 类型,因此你可以首选它,同时该类型也往往是性能最好的。`isize` 和 `usize` 的主要应用场景是用作集合的索引。
|
|
|
|
这么多类型,有没有一个简单的使用准则?答案是肯定的, Rust 整形默认使用 `i32`,例如 `let i = 1`,那 `i` 就是 `i32` 类型,因此你可以首选它,同时该类型也往往是性能最好的。`isize` 和 `usize` 的主要应用场景是用作集合的索引。
|
|
|
|
|
|
|
|
|
|
|
|
> ##### 整型溢出
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
> 比方说有一个 `u8` ,它可以存放从 0 到 255 的值。那么当你将其修改为范围之外的值,比如 256,则会发生**整型溢出**。关于这一行为 Rust 有一些有趣的规则:当在 debug 模式编译时,Rust 会检查整型溢出,若存在这些问题,则使程序在编译时 _panic_(崩溃,Rust 使用这个术语来表明程序因错误而退出)。
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
> 在当使用 `--release` 参数进行 release 模式构建时,Rust **不**检测溢出。相反,当检测到整型溢出时,Rust 会按照补码循环溢出(_two’s complement wrapping_)的规则处理。简而言之,大于该类型最大值的数值会被补码转换成该类型能够支持的对应数字的最小值。比如在 `u8` 的情况下,256 变成 0,257 变成 1,依此类推。程序不会 _panic_,但是该变量的值可能不是你期望的值。依赖这种默认行为的代码都应该被认为是错误的代码。
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
> 要显式处理可能的溢出,可以使用标准库针对原始数字类型提供的这些方法:
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
> - 使用 `wrapping_*` 方法在所有模式下都按照补码循环溢出规则处理,例如 `wrapping_add`
|
|
|
|
|
|
|
|
> - 如果使用 `checked_*` 方法时发生溢出,则返回 `None` 值
|
|
|
|
|
|
|
|
> - 使用 `overflowing_*` 方法返回该值和一个指示是否存在溢出的布尔值
|
|
|
|
|
|
|
|
> - 使用 `saturating_*` 方法使值达到最小值或最大值
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#### 浮点类型
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**浮点类型数字** 是带有小数点的数字,在 Rust 中浮点类型数字也有两种基本类型: `f32` 和 `f64`,分别为 32 位和 64 位大小。默认浮点类型是 `f64`,在现代的 CPU 中它的速度与 `f32` 几乎相同,但精度更高。
|
|
|
|
#### 整型溢出
|
|
|
|
|
|
|
|
|
|
|
|
下面是一个演示浮点数的示例:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
假设有一个 `u8` ,它可以存放从 0 到 255 的值。那么当你将其修改为范围之外的值,比如 256,则会发生**整型溢出**。关于这一行为 Rust 有一些有趣的规则:当在 debug 模式编译时,Rust 会检查整型溢出,若存在这些问题,则使程序在编译时 _panic_(崩溃,Rust 使用这个术语来表明程序因错误而退出)。
|
|
|
|
fn main() {
|
|
|
|
|
|
|
|
let x = 2.0; // f64
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let y: f32 = 3.0; // f32
|
|
|
|
在当使用 `--release` 参数进行 release 模式构建时,Rust **不**检测溢出。相反,当检测到整型溢出时,Rust 会按照补码循环溢出(_two’s complement wrapping_)的规则处理。简而言之,大于该类型最大值的数值会被补码转换成该类型能够支持的对应数字的最小值。比如在 `u8` 的情况下,256 变成 0,257 变成 1,依此类推。程序不会 _panic_,但是该变量的值可能不是你期望的值。依赖这种默认行为的代码都应该被认为是错误的代码。
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
浮点数根据 `IEEE-754` 标准实现。`f32` 类型是单精度浮点型,`f64` 为双精度。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#### 数字运算
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Rust 支持所有数字类型的基本数学运算:加法、减法、乘法、除法和取模运算。下面代码各使用一条 `let` 语句来说明相应运算的用法:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
|
|
|
// 加法
|
|
|
|
|
|
|
|
let sum = 5 + 10;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 减法
|
|
|
|
|
|
|
|
let difference = 95.5 - 4.3;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 乘法
|
|
|
|
要显式处理可能的溢出,可以使用标准库针对原始数字类型提供的这些方法:
|
|
|
|
let product = 4 * 30;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 除法
|
|
|
|
- 使用 `wrapping_*` 方法在所有模式下都按照补码循环溢出规则处理,例如 `wrapping_add`
|
|
|
|
let quotient = 56.7 / 32.2;
|
|
|
|
- 如果使用 `checked_*` 方法时发生溢出,则返回 `None` 值
|
|
|
|
|
|
|
|
- 使用 `overflowing_*` 方法返回该值和一个指示是否存在溢出的布尔值
|
|
|
|
|
|
|
|
- 使用 `saturating_*` 方法使值达到最小值或最大值
|
|
|
|
|
|
|
|
|
|
|
|
// 求余
|
|
|
|
## 浮点类型
|
|
|
|
let remainder = 43 % 5;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
这些语句中的每个表达式都使用了数学运算符,并且计算结果为一个值,然后绑定到一个变量上。[附录 B](https://course.rs/appendix/operators.html#运算符) 中给出了 Rust 提供的所有运算符的列表。
|
|
|
|
**浮点类型数字** 是带有小数点的数字,在 Rust 中浮点类型数字也有两种基本类型: `f32` 和 `f64`,分别为 32 位和 64 位大小。默认浮点类型是 `f64`,在现代的 CPU 中它的速度与 `f32` 几乎相同,但精度更高。
|
|
|
|
|
|
|
|
|
|
|
|
再来看一个综合性的示例:
|
|
|
|
下面是一个演示浮点数的示例:
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
fn main() {
|
|
|
|
fn main() {
|
|
|
|
// 编译器会进行自动推导,给予twenty i32的类型
|
|
|
|
let x = 2.0; // f64
|
|
|
|
let twenty = 20;
|
|
|
|
|
|
|
|
// 类型标注
|
|
|
|
|
|
|
|
let twenty_one: i32 = 21;
|
|
|
|
|
|
|
|
// 通过类型后缀的方式进行类型标注:22是i32类型
|
|
|
|
|
|
|
|
let twenty_two = 22i32;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 只有同样类型,才能运算
|
|
|
|
|
|
|
|
let addition = twenty + twenty_one + twenty_two;
|
|
|
|
|
|
|
|
println!("{} + {} + {} = {}", twenty, twenty_one, twenty_two, addition);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 对于较长的数字,可以用_进行分割,提升可读性
|
|
|
|
|
|
|
|
let one_million: i64 = 1_000_000;
|
|
|
|
|
|
|
|
println!("{}", one_million.pow(2));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 定义一个f32数组,其中42.0会自动被推导为f32类型
|
|
|
|
|
|
|
|
let forty_twos = [
|
|
|
|
|
|
|
|
42.0,
|
|
|
|
|
|
|
|
42f32,
|
|
|
|
|
|
|
|
42.0_f32,
|
|
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 打印数组中第一个值,并控制小数位为2位
|
|
|
|
let y: f32 = 3.0; // f32
|
|
|
|
println!("{:.2}", forty_twos[0]);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
浮点数根据 `IEEE-754` 标准实现。`f32` 类型是单精度浮点型,`f64` 为双精度。
|
|
|
|
|
|
|
|
|
|
|
|
#### 浮点数陷阱
|
|
|
|
#### 浮点数陷阱
|
|
|
|
|
|
|
|
|
|
|
|
浮点数由于底层格式的特殊性,导致了如果在使用浮点数时不够谨慎,就可能造成危险,有两个原因:
|
|
|
|
浮点数由于底层格式的特殊性,导致了如果在使用浮点数时不够谨慎,就可能造成危险,有两个原因:
|
|
|
@ -200,7 +144,89 @@ note: run with `RUST_BACKTRACE=1` environment variable to display
|
|
|
|
|
|
|
|
|
|
|
|
是不是**blow your mind away**? 没关系,在本书的后续章节中类似的直击灵魂的地方还很多,这就是敢号称 `Rust语言圣经(Rust Course)` 的底气!
|
|
|
|
是不是**blow your mind away**? 没关系,在本书的后续章节中类似的直击灵魂的地方还很多,这就是敢号称 `Rust语言圣经(Rust Course)` 的底气!
|
|
|
|
|
|
|
|
|
|
|
|
#### 位运算
|
|
|
|
#### NaN
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
对于数学上未定义的结果,例如对负数取平方根 `-42.1.sqrt()` ,会产生一个特殊的结果:Rust 的浮点数类型使用 `NaN` (not a number)来处理这些情况。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**所有跟 `NaN` 交互的操作,都会返回一个 `NaN`**,而且 `NaN` 不能用来比较,下面的代码会崩溃:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
|
|
|
let x = (-42.0_f32).sqrt();
|
|
|
|
|
|
|
|
assert_eq!(x, x);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
出于防御性编程的考虑,可以使用 `is_nan()` 等方法,可以用来判断一个数值是否是 `NaN` :
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
|
|
|
let x = (-42.0_f32).sqrt();
|
|
|
|
|
|
|
|
if x.is_nan() {
|
|
|
|
|
|
|
|
println!("未定义的数学行为")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 数字运算
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Rust 支持所有数字类型的基本数学运算:加法、减法、乘法、除法和取模运算。下面代码各使用一条 `let` 语句来说明相应运算的用法:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
|
|
|
// 加法
|
|
|
|
|
|
|
|
let sum = 5 + 10;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 减法
|
|
|
|
|
|
|
|
let difference = 95.5 - 4.3;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 乘法
|
|
|
|
|
|
|
|
let product = 4 * 30;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 除法
|
|
|
|
|
|
|
|
let quotient = 56.7 / 32.2;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 求余
|
|
|
|
|
|
|
|
let remainder = 43 % 5;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
这些语句中的每个表达式都使用了数学运算符,并且计算结果为一个值,然后绑定到一个变量上。[附录 B](https://course.rs/appendix/operators.html#运算符) 中给出了 Rust 提供的所有运算符的列表。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
再来看一个综合性的示例:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
|
|
|
// 编译器会进行自动推导,给予twenty i32的类型
|
|
|
|
|
|
|
|
let twenty = 20;
|
|
|
|
|
|
|
|
// 类型标注
|
|
|
|
|
|
|
|
let twenty_one: i32 = 21;
|
|
|
|
|
|
|
|
// 通过类型后缀的方式进行类型标注:22是i32类型
|
|
|
|
|
|
|
|
let twenty_two = 22i32;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 只有同样类型,才能运算
|
|
|
|
|
|
|
|
let addition = twenty + twenty_one + twenty_two;
|
|
|
|
|
|
|
|
println!("{} + {} + {} = {}", twenty, twenty_one, twenty_two, addition);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 对于较长的数字,可以用_进行分割,提升可读性
|
|
|
|
|
|
|
|
let one_million: i64 = 1_000_000;
|
|
|
|
|
|
|
|
println!("{}", one_million.pow(2));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 定义一个f32数组,其中42.0会自动被推导为f32类型
|
|
|
|
|
|
|
|
let forty_twos = [
|
|
|
|
|
|
|
|
42.0,
|
|
|
|
|
|
|
|
42f32,
|
|
|
|
|
|
|
|
42.0_f32,
|
|
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 打印数组中第一个值,并控制小数位为2位
|
|
|
|
|
|
|
|
println!("{:.2}", forty_twos[0]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 位运算
|
|
|
|
|
|
|
|
|
|
|
|
Rust的运算基本上和其他语言一样
|
|
|
|
Rust的运算基本上和其他语言一样
|
|
|
|
|
|
|
|
|
|
|
@ -241,29 +267,6 @@ fn main() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### NaN
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
对于数学上未定义的结果,例如对负数取平方根 `-42.1.sqrt()` ,会产生一个特殊的结果:Rust 的浮点数类型使用 `NaN` (not a number)来处理这些情况。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**所有跟 `NaN` 交互的操作,都会返回一个 `NaN`**,而且 `NaN` 不能用来比较,下面的代码会崩溃:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
|
|
|
let x = (-42.0_f32).sqrt();
|
|
|
|
|
|
|
|
assert_eq!(x, x);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
出于防御性编程的考虑,可以使用 `is_nan()` 等方法,可以用来判断一个数值是否是 `NaN` :
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
|
|
|
let x = (-42.0_f32).sqrt();
|
|
|
|
|
|
|
|
if x.is_nan() {
|
|
|
|
|
|
|
|
println!("未定义的数学行为")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 序列(Range)
|
|
|
|
## 序列(Range)
|
|
|
|
|
|
|
|
|
|
|
|