介绍
数字处理包括所有数字类型: 所有整形和浮点类型
整形
| 有符号类型 | 无符号类型 |
|---|---|
| i8 | u8 |
| i16 | u16 |
| i32 | u32 |
| i64 | i64 |
| i128 | u128 |
| isize | usize |
浮点型
特殊值(常量)
rust
/*
所有的整形类型都有3个常量:
MAX: 当前类型的最大值
MIN: 当前类型的最小值
BITS: 当前类型所占用的位数
所有的浮点数类型都有14个常量, 最常用的有:
MAX: 最大有限值
MIN: 最小有限值
INFINITY: 无穷大
NEG_INFINITY: 无穷小(负无穷大)
MIN_POSITIVE: 最小正数值
MAX_POSITIVE: 最大正数值
NAN: 非数值
*/
fn main() {
println!("{}", i8::MAX); // 最大值 127
println!("{}", i8::MIN); // 最小值 -128
println!("{}", i8::BITS); // 一个 i8 数字占用的位数(8)
println!("{}", u8::MAX); // 最大值 255
println!("{}", u8::MIN); // 最小值 0
println!("{}", u8::BITS); // 一个 u8 数字占用的位数(8)
println!("{}", i32::MAX); // 最大值 2147483647
println!("{}", i32::MIN); // 最小值 -2147483648
println!("{}", i32::BITS); // 一个 i8 数字占用的位数(32)
println!("{}", f32::MAX); // 最大有限值 3.40282347e+38
println!("{}", f32::MIN); // 最小有限值 1.40129846e-45
println!("{}", f32::INFINITY); // 无穷大
println!("{}", f32::NEG_INFINITY); // 无穷小(负无穷大)
println!("{}", f32::MIN_POSITIVE); // 最小正数值
println!("{}", f32::NAN); // 非数值
}基本运算
直接使用运算符的问题
rust
/*
直接使用运算符会导致代码检查器通过, 但是无法通过编译
这里以 + 为例说明:
直接使用 a + b 调用的是 std::ops::Add trait
它不会检查数值的大小, 而是直接相加, 则会导致在编写代码的时候
无法发现错误, 然后编译失败
*/
fn main() {
let a: i8 = 120;
let b: i8 = 10;
// 这个代码在编辑器中是不会报错的
let x: i8 = a + b;
println!("{}", x);
}
/*
执行 cargo run, 报错信息如下:
Compiling rust_demo v0.0.1 (/Users/codes/rust_demo)
error: this arithmetic operation will overflow
--> src/main.rs:16:17
|
16 | let x: i8 = a + b;
| ^^^^^ attempt to compute `120_i8 + 10_i8`, which would overflow
|
= note: `#[deny(arithmetic_overflow)]` on by default
error: could not compile `rust_demo` (bin "rust_demo") due to 1 previous error
*/检查运算 checked_*
- 使用这种方法可以避免直接使用运算符带来的编译不通过问题
- check_* 函数非常多, 但是常用的就这几个, 有其他需要可以看文档
- 比如计算 n 次方:
checked_pow()
| 函数 | 作用 |
|---|---|
| checked_add | 加法运算, 返回一个 Option 值, 如果溢出返回 None |
| checked_sub | 减法运算, 返回一个 Option 值, 如果溢出返回 None |
| checked_mul | 减法运算, 返回一个 Option 值, 如果溢出返回 None |
| checked_div | 除法运算, 返回一个 Option 值, 如果溢出返回 None |
| checked_rem | 取余运算, 返回一个 Option 值, 如果溢出返回 None |
rust
fn main() {
let a: u8 = 200;
if let Some(x) = a.checked_add(100) {
// 溢出返回 None
println!("x = {}", x);
}
if let Some(x) = a.checked_add(1) {
// 没溢出返回 Some(x)
println!("x = {}", x);
}
}饱和运算 saturating_*
| 函数 | 作用 |
|---|---|
| saturating_add | 加法运算, 溢出取最大/小值, 否则正常运算 |
| saturating_sub | 减法运算, 溢出取最大/小值, 否则正常运算 |
| saturating_mul | 减法运算, 溢出取最大/小值, 否则正常运算 |
| saturating_div | 除法运算, 溢出取最大/小值, 否则正常运算 |
| saturating_rem | 取余运算, 溢出取最大/小值, 否则正常运算 |
rust
fn main() {
let a: u8 = 200;
// a + 100 = 300, 但是 u8 最大值是 255, 所以 x 是 255
let x = a.saturating_add(100);
println!("a + 100 = {x}");
// a + 1 = 201, 没有溢出, 所以 y 是 201
let y = a.saturating_add(1);
println!("a + 100 = {y}");
// a - 250 = -50, 但是 u8 最小值是 0, 所以 z 是 0
let z = a.saturating_sub(250);
println!("a - 300 = {z}");
}回绕运算 wrapping_*
| 函数 | 作用 |
|---|---|
| saturating_add | 加法运算, 溢出时使用类型的边界处回绕, 否则正常运算 |
| saturating_sub | 减法运算, 溢出时使用类型的边界处回绕, 否则正常运算 |
| saturating_mul | 减法运算, 溢出时使用类型的边界处回绕, 否则正常运算 |
| saturating_div | 除法运算, 溢出时使用类型的边界处回绕, 否则正常运算 |
| saturating_rem | 取余运算, 溢出时使用类型的边界处回绕, 否则正常运算 |
rust
fn main() {
// i8 类型的最大值是 127, 最小值是 -128
// 127 + 1 溢出, 则取 最小值+0 -> (-127 + 0)
// 127 + 2 溢出, 则取 最小值+1 -> (-127 + 1)
// 127 + 3 溢出, 则取 最小值+2 -> (-127 + 2)
let a: i8 = i8::MAX; // 127
let a1 = a.wrapping_add(1); // -128
let a2 = a.wrapping_add(2); // -127
let a3 = a.wrapping_add(3); // -126
println!("a1 = {a1}, a2 = {a2}, a3 = {a3}");
// u8 类型的最大值是 255, 最小值是 0
let b: u8 = u8::MAX; // 255
let b1 = b.wrapping_add(1); // 0
let b2 = b.wrapping_add(2); // 1
let b3 = b.wrapping_add(3); // 2
println!("b1 = {b1}, b2 = {b2}, b3 = {b3}");
}溢出运算 overflowing_*
| 函数 | 作用 |
|---|---|
| overflowing_add | 加法运算, 返回元组(回绕运算后的值, 是否溢出) |
| overflowing_sub | 减法运算, 返回元组(回绕运算后的值, 是否溢出) |
| overflowing_mul | 减法运算, 返回元组(回绕运算后的值, 是否溢出) |
| overflowing_div | 除法运算, 返回元组(回绕运算后的值, 是否溢出) |
| overflowing_rem | 取余运算, 返回元组(回绕运算后的值, 是否溢出) |
rust
fn main() {
let a: i8 = 125;
let (num, is_overflow) = a.overflowing_add(1);
if is_overflow {
println!("Overflow: {num}");
} else {
println!("Result: {num}"); // Result: 126
}
let (num, is_overflow) = a.overflowing_add(3);
if is_overflow {
println!("Overflow: {num}"); // Overflow! num=-128
} else {
println!("Result: {num}");
}
}位运算
在一些性能特别敏感的场景可能需要这些函数, 比如: 哈希算法/游戏引擎/绘制图形
| 函数 | 作用 | 注意点 |
|---|---|---|
| signum | 获取符号位数字(正数:1/负数:-1) | 注意不是返回 0 和 1 |
| rotate_left | 左移指定的量 n | 此操作与 << n 移位运算符不同 |
| rotate_right | 右移指定的量 n | 此操作与 >> n 移位运算符不同 |
| swap_bytes | 字节交换 | |
| reverse_bits | 位反转 |
判断正负
3个函数都可以判断一个数字是正数还是负数
| 函数 | 作用 |
|---|---|
| signum | 获取符号位数字(正数:1/负数:0) |
| is_negative | 是否是负数 |
| is_positive | 是否是正数 |
rust
fn main() {
let a: i32 = 10;
println!("a is is_positive: {:?}", a.is_positive()); // true
println!("a is is_positive: {:?}", a.is_negative()); // false
println!("a signum is: {}", a.signum()); // 1
let b: i32 = -5;
println!("b is is_positive: {:?}", b.is_positive()); // false
println!("b is is_positive: {:?}", b.is_negative()); // true
println!("b signum is: {}", b.signum()); // -1
}比较大小
所有数字类型都实现了比较大小的 Trait, 如
- Eq
- Ord 提供 max/min/clamp 方法
- PartialEq提供 eq/ne 方法
- PartialOrd 提供 gt/lt/ge/le 方法
所以这些 trait 定义的方法, 数字类型都可以直接使用
rust
fn main() {
let a: i32 = 10;
let b: i32 = 20;
// 可以比较两个值是否相等/不等
// 需要实现 std::cmp::PartialEq 这个接口, 实现这个接口后
// 只要实现这个接口后, 会自动实现 std::cmp::PartialEq 接口
// 可以直接使用 == 和 != 来比较两个值, 不用调用方法
println!("a == a: {}", a.eq(&b)); // false
println!("a != b: {}", a.ne(&b)); // true
println!("a == b: {}", a == b); // false
println!("a != b: {}", a != b); // true
// 对比两个值, 哪个值更大/更小就返回哪个
// 必须实现: std::cmp::Ord 这个接口
println!("a.max(b): {}", a.max(b)); // 20
println!("a.min(b): {}", a.min(b)); // 10
// 控制一个值必须在范围内 n.clamp(min, max)
// 如果 n 小于 min 则返回 min
// 如果 n 大于 max 则返回 max
// 如果 n 不小于 min,也不大于 max, 直接返回 n
// 必须实现: std::cmp::PartialOrd 这个接口
let n = 5;
println!("n.clamp(a, b): {}", n.clamp(a, b)); // x小于min, 所以返回 min -> 10
let n = 30;
println!("n.clamp(a, b): {}", n.clamp(a, b)); // x大于max, 所以返回 max -> 20
let n = 15;
println!("n.clamp(a, b): {}", n.clamp(a, b)); // x在 min 和 max 之间, 所以返回 x -> 15
// 判读两个值 大于/小于/大于等于/小于等于
// 必须实现: std::cmp::PartialOrd 这个接口
println!("a > b: {}", a > b); // false
println!("a < b: {}", a < b); // true
println!("a >= b: {}", a >= b); // false
println!("a <= b: {}", a <= b); // true
println!("b > a: {}", a.gt(&b)); // true
println!("b < a: {}", a.lt(&b)); // false
println!("b >= a: {}", a.ge(&b)); // false
println!("b <= a: {}", a.le(&b)); // true
}浮点数常用方法
这些函数是 f32 和 f64 专属的方法, 其他整数类型是没有的, 比如: 向上取整/向下取整/四舍五入 还有一些数学专用的方法, 比如: 反余弦函数,反双曲余弦函数 这些就自己看文档吧
| 函数 | 作用 |
|---|---|
| ceil | 向上取整 |
| floor | 向下取整 |
| round | 四舍五入 |
| trunc | 截断小数部分 |
| fract | 获取小数部分 |
| is_nan | 是否是常量 NaN |
| is_infinite | 非无穷大或 Nan |
rust
fn main() {
let a: f32 = 1.1;
let b: f32 = 1.6;
println!("a.floor() = {}", a.floor()); // 1
println!("b.floor() = {}", b.floor()); // 1
println!("a.ceil() = {}", a.ceil()); // 2
println!("b.ceil() = {}", b.ceil()); // 2
println!("a.round() = {}", a.round()); // 1
println!("b.round() = {}", b.round()); // 2
println!("a.trunc() = {}", a.trunc()); // 1
println!("b.trunc() = {}", b.trunc()); // 1
println!("a.fract() = {}", a.fract()); // 0.100000024 // 说明有浮点数精确度问题
println!("b.fract() = {}", b.fract()); // 0.6
let c: f32 = 3.15;
let inif: f32 = f32::INFINITY;
let nan: f32 = f32::NAN;
println!("c.is_finite() = {}", c.is_finite()); // true
println!("c.is_infinite() = {}", c.is_infinite()); // false
println!("c.is_nan() = {}", c.is_nan()); // false
println!("inif.is_finite() = {}", inif.is_finite()); // false
println!("inif.is_infinite() = {}", inif.is_infinite()); // true
println!("inif.is_nan() = {}", inif.is_nan()); // false
println!("nan.is_finite() = {}", nan.is_finite()); // false
println!("nan.is_infinite() = {}", nan.is_infinite()); // false
println!("nan.is_nan() = {}", nan.is_nan()); // true
}解析与转换
数字类型之间的转换
直接使用 as 关键字来断言类型
rust
fn main() {
let a: f32 = 1.2;
let b = a.floor() as i32;
println!("b = {}", b); // 1
}数字格式化为字符串
rust
fn main() {
let num = 100;
let formated = format!("{num:b}"); // 格式化为2进制字符串
println!("{formated}"); // 100100
let formated = format!("{num:o}"); // 格式化为8进制字符串
println!("{formated}"); // 144
let formated = format!("{num:x}"); // 格式化为16进制字符串
println!("{formated}"); // 64
let fnum = 1.2;
let formated = format!("{fnum:e}"); // 格式化为指数表示(科学计数法)
println!("{formated}"); // 1.200000e+00
let formated = format!("{fnum:E}"); // 格式化为指数表示(数字计数法)
println!("{formated}"); // 1.200000E+00
}字符串解析为数字
这是通过 str 类型的 parse方法 实现的, 因为所有的数字都实现了 FromStr 接口
rust
fn main() {
let num = "42".parse::<i32>().unwrap();
println!("num = {}", num); // 42
"0x2A".parse::<i32>().unwrap();
println!("num = {}", num); // 42
"0b101010".parse::<i32>().unwrap();
println!("num = {}", num); // 42
}为自定义结构类型实现比较大小接口
rust
struct Point {
x: u32,
y: u32,
}
// 为 Point 类型实现: == 和 !==, Ord/PartialOrd 也是这样使用的
impl std::cmp::PartialEq for Point {
// 只需要实现 eq 即可, 因为 ne 就是 eq 的结果取反
fn eq(&self, other: &Self) -> bool {
self.x == other.x && self.y == other.y
}
}
fn main() {
let p1 = Point { x: 1, y: 2 };
let p2 = Point { x: 1, y: 2 };
if p1 == p2 {
println!("p1 == p2");
}
if p1 != p2 {
println!("p1 != p2");
}
if p1.eq(&p2) {
println!("p1.eq(&p2)");
}
if p1.ne(&p2) {
println!("p1.ne(&p2)");
}
}rust
#[derive(PartialEq)]
struct Point {
x: u32,
y: u32,
}
// derive[PartialEq]派生宏最终也会展开成这样:
// 因为默认实现就是对比 struct 中的每一个字段
// impl std::cmp::PartialEq for Point {
// fn eq(&self, other: &Self) -> bool {
// self.x == other.x && self.y == other.y
// }
// }
fn main() {
let p1 = Point { x: 1, y: 2 };
let p2 = Point { x: 1, y: 2 };
if p1 == p2 {
println!("p1 == p2");
}
if p1 != p2 {
println!("p1 != p2");
}
if p1.eq(&p2) {
println!("p1.eq(&p2)");
}
if p1.ne(&p2) {
println!("p1.ne(&p2)");
}
}数字处理相关的开源库
随机数
rust
fn main() {
// 获取u8类型的随机数
let x: u8 = rand::random();
println!("x = {x}");
// 获取i32类型的随机数
let x: i32 = rand::random();
println!("x = {x}");
// 获取u8类型指定范围内的随机数
let x: u8 = rand::random_range(1..10);
println!("x = {x}");
// 获取i32类型指定范围内的随机数
let x: i32 = rand::random_range(1..1000);
println!("x = {x}");
}高精度计算
比如计算余额