Skip to content

介绍

数字处理包括所有数字类型: 所有整形和浮点类型

整形

有符号类型无符号类型
i8u8
i16u16
i32u32
i64i64
i128u128
isizeusize

浮点型

特殊值(常量)

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, 如

所以这些 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}");
}

高精度计算

比如计算余额

Released under the MIT License.