【Rust】浮点数详解

1. 浮点数类型概述

1.1 两种浮点类型

Rust 提供两种符合 IEEE 754 标准的浮点数类型:

类型 位宽 精度 范围(近似) 默认
f32 32位 单精度 ±3.4×10³⁸
f64 64位 双精度 ±1.8×10³⁰⁸

为什么默认是 f64?

在现代 CPU 上,f64f32 性能相近,但提供更高精度和更大范围。

rust 复制代码
fn float_type_basics() {
    // 显式类型标注
    let x: f32 = 3.14;
    let y: f64 = 2.71828;
    
    // 类型推断(默认为 f64)
    let a = 3.14;        // f64
    let b = 2.0;         // f64
    let c = 1.0_f32;     // f32(使用后缀)
    
    // 内存大小
    println!("内存占用:");
    println!("  f32: {} 字节", std::mem::size_of::<f32>());
    println!("  f64: {} 字节", std::mem::size_of::<f64>());
    
    // 范围限制
    println!("\n数值范围:");
    println!("  f32 最小值: {}", f32::MIN);
    println!("  f32 最大值: {}", f32::MAX);
    println!("  f64 最小值: {}", f64::MIN);
    println!("  f64 最大值: {}", f64::MAX);
    
    // 特殊值
    println!("\n特殊值:");
    println!("  f32 正无穷大: {}", f32::INFINITY);
    println!("  f32 负无穷大: {}", f32::NEG_INFINITY);
    println!("  f32 非数字 (NaN): {}", f32::NAN);
}

1.2 IEEE 754 标准概述

Rust 浮点数严格遵循 IEEE 754 标准,包含三个部分:

复制代码
f32 内存布局(32位):
┌───┬───┬───┐
│ S │ E │ M │
└───┴───┴───┘
S: 符号位 (1位)  0=正数, 1=负数
E: 指数位 (8位)  偏移 127
M: 尾数位 (23位) 隐含的前导1

f64 内存布局(64位):
┌───┬─────┬─────┐
│ S │ E   │ M   │
└───┴─────┴─────┘
S: 符号位 (1位)   0=正数, 1=负数
E: 指数位 (11位)  偏移 1023
M: 尾数位 (52位)  隐含的前导1
rust 复制代码
fn ieee754_demo() {
    // 查看浮点数的二进制表示
    let num: f32 = 3.14;
    let bits = num.to_bits();
    
    println!("数值 {} 的 IEEE 754 表示:", num);
    println!("  十六进制: 0x{:08X}", bits);
    
    // 解析各部分
    let sign = (bits >> 31) & 0x1;
    let exponent = (bits >> 23) & 0xFF;
    let mantissa = bits & 0x7FFFFF;
    
    println!("  符号位: {}", sign);
    println!("  指数位: {} (偏置: {})", exponent, exponent as i32 - 127);
    println!("  尾数位: 0x{:06X}", mantissa);
    
    // 特殊值的表示
    println!("\n特殊值表示:");
    println!("  正无穷: 0x{:08X}", f32::INFINITY.to_bits());
    println!("  负无穷: 0x{:08X}", f32::NEG_INFINITY.to_bits());
    println!("  NaN: 0x{:08X}", f32::NAN.to_bits());
}

2. 浮点数字面量

2.1 基本字面量格式

Rust 提供多种浮点数字面量表示方式。

rust 复制代码
fn float_literals() {
    println!("=== 浮点数字面量 ===");
    
    // 1. 基本小数形式
    let standard = 3.14;         // f64
    let with_zero = 0.5;         // f64
    let no_leading = .75;        // 省略前导0
    let no_trailing = 42.;       // 省略尾随0
    
    // 2. 科学计数法
    let scientific = 1.23e4;     // 1.23 × 10⁴ = 12300.0
    let negative_exp = 5.67e-3;  // 5.67 × 10⁻³ = 0.00567
    let capital_e = 8.91E2;      // 大写 E 也可用
    
    // 3. 类型后缀
    let f32_suffix = 3.14_f32;
    let f64_suffix = 2.718_f64;
    
    // 4. 下划线增强可读性
    let readable = 1_000_000.0;
    let precise = 3.141_592_653_589_793;
    
    // 显示所有示例
    let examples = [
        ("标准小数", "3.14", 3.14),
        ("科学计数", "1.23e4", 1.23e4),
        ("负指数", "5.67e-3", 5.67e-3),
        ("带下划线", "3.141_592", 3.141_592),
        ("f32类型", "1.0_f32", 1.0_f32),
    ];
    
    for (desc, literal, _) in &examples {
        println!("  {}: {}", desc, literal);
    }
    
    // 实际数值验证
    println!("\n实际数值:");
    for (desc, _, value) in examples {
        println!("  {} = {}", desc, value);
    }
}

2.2 特殊值字面量

Rust 支持 IEEE 754 定义的特殊值。

rust 复制代码
fn special_value_literals() {
    println!("=== 特殊值字面量 ===");
    
    // 1. 无穷大
    let pos_inf = f32::INFINITY;
    let neg_inf = f32::NEG_INFINITY;
    
    println!("无穷大:");
    println!("  正无穷: {}", pos_inf);
    println!("  负无穷: {}", neg_inf);
    
    // 2. 非数字 (NaN)
    let nan = f32::NAN;
    let another_nan = 0.0 / 0.0;  // 产生 NaN
    
    println!("\n非数字 (NaN):");
    println!("  f32::NAN = {}", nan);
    println!("  0.0 / 0.0 = {}", another_nan);
    
    // 3. 检查函数
    println!("\n检查函数:");
    println!("  NaN.is_nan() = {}", nan.is_nan());
    println!("  INFINITY.is_infinite() = {}", pos_inf.is_infinite());
    println!("  3.14.is_finite() = {}", 3.14.is_finite());
    
    // 4. 生成特殊值的运算
    println!("\n生成特殊值的运算:");
    println!("  1.0 / 0.0 = {}", 1.0 / 0.0);      // 正无穷
    println!("  -1.0 / 0.0 = {}", -1.0 / 0.0);    // 负无穷
    println!("  0.0 / 0.0 = {}", 0.0 / 0.0);      // NaN
    println!("  f32::INFINITY * 0.0 = {}", f32::INFINITY * 0.0);  // NaN
    
    // 5. 最小正正规数和最小正值
    println!("\n极小值:");
    println!("  f32 最小正正规数: {}", f32::MIN_POSITIVE);
    println!("  f32 最小正值: {}", f32::from_bits(1));  // 非正规数
    println!("  f64 最小正正规数: {}", f64::MIN_POSITIVE);
}

2.3 进制表示与转换

支持不同进制的浮点数字面量。

rust 复制代码
fn radix_literals() {
    println!("=== 进制表示 ===");
    
    // 注意:Rust 不支持二进制、八进制、十六进制的浮点数字面量
    // 但可以在整数和浮点数间转换
    
    // 1. 从整数转换
    let from_int: f32 = 42 as f32;
    let explicit: f64 = 100_f64;
    
    // 2. 解析字符串
    let parsed: f64 = "3.14".parse().unwrap();
    let sci_parsed: f32 = "1.23e4".parse().unwrap();
    
    // 3. 显示不同格式
    let value = 1234.5678;
    
    println!("格式化显示:");
    println!("  默认: {}", value);
    println!("  科学计数法: {:e}", value);
    println!("  十六进制: {:x}", value.to_bits());  // 显示位模式
    
    // 4. 精度控制
    println!("\n精度控制:");
    println!("  2位小数: {:.2}", 3.14159);
    println!("  总宽度10, 右对齐: {:>10.2}", 3.14159);
    println!("  科学计数法2位小数: {:.2e}", 1234567.89);
    
    // 5. 进制转换注意事项
    let decimal = 0.1;
    let binary_repr = format!("{:b}", decimal.to_bits());
    
    println!("\n十进制 0.1 的二进制表示:");
    println!("  f32: {} 位", binary_repr.len());
    println!("  注意: 0.1 无法精确用二进制表示!");
}

3. 浮点数运算

3.1 基本算术运算

浮点数支持标准算术运算,但需注意精度问题。

rust 复制代码
fn basic_arithmetic() {
    println!("=== 基本算术运算 ===");
    
    let a = 10.5;
    let b = 3.2;
    
    // 四则运算
    println!("四则运算:");
    println!("  {} + {} = {}", a, b, a + b);
    println!("  {} - {} = {}", a, b, a - b);
    println!("  {} × {} = {}", a, b, a * b);
    println!("  {} ÷ {} = {}", a, b, a / b);
    println!("  {} % {} = {}", a, b, a % b);  // 求余
    
    // 复合赋值
    let mut x = 5.0;
    x += 2.5;
    x -= 1.0;
    x *= 2.0;
    x /= 4.0;
    println!("\n复合赋值: 最终 x = {}", x);
    
    // 一元运算
    println!("\n一元运算:");
    println!("  -{} = {}", a, -a);
    println!("  +{} = {}", a, +a);
    
    // 幂运算
    println!("\n幂运算:");
    let base = 2.0;
    println!("  {}^{} = {}", base, 3, base.powi(3));   // 整数幂
    println!("  {}^{} = {}", base, 0.5, base.powf(0.5)); // 浮点幂
    println!("  e^{} = {}", 1.0, 1.0_f64.exp());       // 指数函数
    println!("  ln({}) = {}", 2.71828, 2.71828_f64.ln()); // 自然对数
}

3.2 数学函数

Rust 标准库提供丰富的数学函数。

rust 复制代码
fn math_functions() {
    println!("=== 数学函数 ===");
    
    // 1. 三角函数(参数为弧度)
    let angle = std::f64::consts::PI / 4.0;  // 45度
    
    println!("三角函数 (π/4 弧度):");
    println!("  sin({}) = {:.4}", angle, angle.sin());
    println!("  cos({}) = {:.4}", angle, angle.cos());
    println!("  tan({}) = {:.4}", angle, angle.tan());
    
    // 反三角函数
    let value = 0.7071;
    println!("\n反三角函数:");
    println!("  asin({}) = {:.4}", value, value.asin());
    println!("  acos({}) = {:.4}", value, value.acos());
    println!("  atan({}) = {:.4}", value, value.atan());
    println!("  atan2(1, 1) = {:.4}", 1.0_f64.atan2(1.0));
    
    // 2. 双曲函数
    let x = 1.0;
    println!("\n双曲函数:");
    println!("  sinh({}) = {:.4}", x, x.sinh());
    println!("  cosh({}) = {:.4}", x, x.cosh());
    println!("  tanh({}) = {:.4}", x, x.tanh());
    
    // 3. 其他数学函数
    let num = 3.7;
    println!("\n其他函数:");
    println!("  平方根 √{} = {:.4}", num, num.sqrt());
    println!("  立方根 ∛{} = {:.4}", num, num.cbrt());
    println!("  绝对值 |{}| = {}", -num, (-num).abs());
    
    // 4. 舍入函数
    let value = 3.756;
    println!("\n舍入函数:");
    println!("  向下取整 floor({}) = {}", value, value.floor());
    println!("  向上取整 ceil({}) = {}", value, value.ceil());
    println!("  四舍五入 round({}) = {}", value, value.round());
    println!("  截断取整 trunc({}) = {}", value, value.trunc());
    println!("  取小数部分 fract({}) = {}", value, value.fract());
}

3.3 位运算与特殊运算

浮点数的位级操作和特殊运算。

rust 复制代码
fn bitwise_and_special_ops() {
    println!("=== 位运算与特殊运算 ===");
    
    // 1. 位模式操作
    let num: f32 = 3.14;
    let bits = num.to_bits();
    let from_bits = f32::from_bits(bits);
    
    println!("位模式操作:");
    println!("  {}.to_bits() = 0x{:08X}", num, bits);
    println!("  f32::from_bits(0x{:08X}) = {}", bits, from_bits);
    
    // 2. 符号操作
    let positive = 3.14;
    let negative = -2.71;
    
    println!("\n符号操作:");
    println!("  {}.abs() = {}", negative, negative.abs());
    println!("  {}.signum() = {}", positive, positive.signum());
    println!("  {}.signum() = {}", negative, negative.signum());
    println!("  {}.copysign({}) = {}", positive, negative, positive.copysign(negative));
    
    // 3. 融合乘加 (FMA) - 更高精度
    #[cfg(target_feature = "fma")]
    {
        let a = 1.5;
        let b = 2.5;
        let c = 3.5;
        let fma_result = a.mul_add(b, c);  // a * b + c,单次舍入
        let normal = a * b + c;            // 两次舍入
        
        println!("\n融合乘加 (FMA):");
        println!("  {}.mul_add({}, {}) = {}", a, b, c, fma_result);
        println!("  {} * {} + {} = {}", a, b, c, normal);
        println!("  差异: {}", (fma_result - normal).abs());
    }
    
    // 4. 最小/最大操作(处理 NaN)
    let x = 3.0;
    let y = f64::NAN;
    let z = 5.0;
    
    println!("\nNaN 安全的比较:");
    println!("  {}.max({}) = {}", x, z, x.max(z));
    println!("  {}.min({}) = {}", x, z, x.min(z));
    println!("  {}.max({}) = {} (NaN 被忽略)", x, y, x.max(y));
    println!("  {}.min({}) = {} (NaN 被忽略)", x, y, x.min(y));
    
    // 5. 次正规数处理
    let tiny: f32 = f32::from_bits(1);  // 最小正次正规数
    println!("\n次正规数:");
    println!("  最小正次正规数: {}", tiny);
    println!("  {} + {} = {}", tiny, tiny, tiny + tiny);
    println!("  {}.is_normal() = {}", tiny, tiny.is_normal());
}

4. 浮点数特性与陷阱

4.1 精度丢失问题

浮点数的有限精度导致的经典问题。

rust 复制代码
fn precision_issues() {
    println!("=== 精度丢失问题 ===");
    
    // 1. 十进制到二进制的转换误差
    let decimal_01 = 0.1;
    let sum = decimal_01 + decimal_01 + decimal_01;
    
    println!("经典 0.1 累加问题:");
    println!("  0.1 + 0.1 + 0.1 = {}", sum);
    println!("  期望值: 0.3");
    println!("  实际值: {}", sum);
    println!("  差异: {}", (sum - 0.3).abs());
    
    // 2. 大数吃小数
    let large = 1e16;
    let small = 1.0;
    
    println!("\n大数吃小数问题:");
    println!("  {} + {} = {}", large, small, large + small);
    println!("  ({} + {}) - {} = {}", large, small, large, (large + small) - large);
    
    // 3. 灾难性抵消
    let a = 1.000_001;
    let b = 1.000_000;
    
    println!("\n灾难性抵消:");
    println!("  {} - {} = {}", a, b, a - b);
    println!("  相对误差: {:.2e}", (a - b - 0.000_001).abs() / 0.000_001);
    
    // 4. 逐步累加的误差
    let mut total: f32 = 0.0;
    for _ in 0..10_000 {
        total += 0.1;
    }
    
    println!("\n逐步累加误差:");
    println!("  0.1 累加 10000 次:");
    println!("  期望值: 1000.0");
    println!("  实际值: {}", total);
    println!("  绝对误差: {}", (total - 1000.0).abs());
    
    // 5. 显示精度限制
    let precise = 0.12345678901234567890;
    println!("\n显示精度:");
    println!("  默认显示: {}", precise);
    println!("  全精度显示: {:.20}", precise);
}

4.2 特殊值行为

NaN 和无穷大的特殊行为。

rust 复制代码
fn special_value_behavior() {
    println!("=== 特殊值行为 ===");
    
    let nan = f64::NAN;
    let inf = f64::INFINITY;
    let neg_inf = f64::NEG_INFINITY;
    
    // 1. NaN 的传播特性
    println!("NaN 传播:");
    println!("  NaN + 1.0 = {}", nan + 1.0);
    println!("  NaN * 2.0 = {}", nan * 2.0);
    println!("  NaN / NaN = {}", nan / nan);
    println!("  sqrt(NaN) = {}", nan.sqrt());
    
    // 2. NaN 的比较特性
    println!("\nNaN 比较:");
    println!("  NaN == NaN: {}", nan == nan);
    println!("  NaN != NaN: {}", nan != nan);
    println!("  NaN < 1.0: {}", nan < 1.0);
    println!("  NaN > 1.0: {}", nan > 1.0);
    println!("  使用 is_nan(): {}", nan.is_nan());
    
    // 3. 无穷大运算
    println!("\n无穷大运算:");
    println!("  ∞ + 1 = {}", inf + 1.0);
    println!("  ∞ × 2 = {}", inf * 2.0);
    println!("  ∞ / ∞ = {}", inf / inf);        // NaN
    println!("  1 / ∞ = {}", 1.0 / inf);        // 0
    println!("  ∞ + (-∞) = {}", inf + neg_inf); // NaN
    
    // 4. 下溢和上溢
    println!("\n溢出检查:");
    let max = f32::MAX;
    let min = f32::MIN_POSITIVE;
    
    println!("  上溢: {} × 2 = {}", max, max * 2.0);  // 无穷大
    println!("  下溢: {} / 2 = {}", min, min / 2.0);  // 0(次正规数)
    
    // 5. 检查函数
    let values = [3.14, inf, neg_inf, nan, 0.0];
    
    println!("\n值检查:");
    for &v in &values {
        println!("  {}:", v);
        println!("    is_nan: {}", v.is_nan());
        println!("    is_infinite: {}", v.is_infinite());
        println!("    is_finite: {}", v.is_finite());
        println!("    is_normal: {}", v.is_normal());
        println!("    is_sign_positive: {}", v.is_sign_positive());
        println!("    is_sign_negative: {}", v.is_sign_negative());
    }
}

4.3 舍入模式与误差

浮点数的舍入规则及其影响。

rust 复制代码
fn rounding_and_error() {
    println!("=== 舍入与误差 ===");
    
    // 1. 默认舍入模式(最近偶数)
    println!("IEEE 754 默认舍入: 最近偶数 (Round to Nearest, ties to Even)");
    
    let examples = [
        (1.4, "向下舍入"),
        (1.6, "向上舍入"),
        (2.5, "平局情况: 向偶数舍入 -> 2"),
        (3.5, "平局情况: 向偶数舍入 -> 4"),
    ];
    
    for (value, desc) in &examples {
        println!("  {} ({}) -> {}", value, desc, value.round());
    }
    
    // 2. 不同舍入方法比较
    let num = 3.756;
    
    println!("\n不同舍入方法:");
    println!("  原始值: {}", num);
    println!("  floor(): {}", num.floor());
    println!("  ceil(): {}", num.ceil());
    println!("  round(): {}", num.round());
    println!("  trunc(): {}", num.trunc());
    
    // 3. 机器精度
    println!("\n机器精度 (Machine Epsilon):");
    println!("  f32 ε: {}", f32::EPSILON);
    println!("  f64 ε: {}", f64::EPSILON);
    println!("  含义: 1.0 + ε > 1.0 的最小正数");
    
    // 验证机器精度
    let eps_f32 = f32::EPSILON;
    println!("\n验证 f32 机器精度:");
    println!("  1.0 + ε = {}", 1.0_f32 + eps_f32);
    println!("  1.0 + ε/2 = {}", 1.0_f32 + eps_f32 / 2.0);
    println!("  (1.0 + ε) > 1.0: {}", (1.0_f32 + eps_f32) > 1.0_f32);
    println!("  (1.0 + ε/2) > 1.0: {}", (1.0_f32 + eps_f32 / 2.0) > 1.0_f32);
    
    // 4. 相对误差计算
    let computed = 3.141592;
    let exact = std::f64::consts::PI;
    
    println!("\n相对误差计算:");
    println!("  计算值: {}", computed);
    println!("  精确值: {:.15}", exact);
    let abs_error = (computed - exact).abs();
    let rel_error = abs_error / exact;
    println!("  绝对误差: {:.2e}", abs_error);
    println!("  相对误差: {:.2e}", rel_error);
}

5. 浮点数实用方法

5.1 安全运算方法

避免 NaN 和无穷大传播的实用方法。

rust 复制代码
fn safe_operations() {
    println!("=== 安全运算方法 ===");
    
    // 1. 检查运算结果
    fn safe_divide(a: f64, b: f64) -> Option<f64> {
        if b == 0.0 {
            None
        } else {
            Some(a / b)
        }
    }
    
    println!("安全除法:");
    match safe_divide(10.0, 2.0) {
        Some(result) => println!("  10.0 / 2.0 = {}", result),
        None => println!("  除零错误"),
    }
    
    match safe_divide(10.0, 0.0) {
        Some(result) => println!("  结果: {}", result),
        None => println!("  10.0 / 0.0 = 除零错误"),
    }
    
    // 2. 钳制函数 (Clamping)
    let value = 15.0;
    let min = 0.0;
    let max = 10.0;
    
    println!("\n数值钳制:");
    println!("  {}.clamp({}, {}) = {}", value, min, max, value.clamp(min, max));
    println!("  {}.clamp({}, {}) = {}", -5.0, min, max, (-5.0).clamp(min, max));
    
    // 3. 线性插值 (Lerp)
    fn lerp(start: f64, end: f64, t: f64) -> f64 {
        start + (end - start) * t.clamp(0.0, 1.0)
    }
    
    println!("\n线性插值:");
    println!("  lerp(0, 10, 0.3) = {}", lerp(0.0, 10.0, 0.3));
    println!("  lerp(0, 10, 1.5) = {}", lerp(0.0, 10.0, 1.5));  // 自动钳制
    
    // 4. 检查近似相等
    fn approx_eq(a: f64, b: f64, epsilon: f64) -> bool {
        (a - b).abs() <= epsilon
    }
    
    let a = 0.1 + 0.1 + 0.1;
    let b = 0.3;
    
    println!("\n近似相等检查:");
    println!("  {} == {}: {}", a, b, a == b);
    println!("  approx_eq({}, {}, 1e-10): {}", a, b, approx_eq(a, b, 1e-10));
    println!("  approx_eq({}, {}, 1e-15): {}", a, b, approx_eq(a, b, 1e-15));
    
    // 5. 避免大数吃小数的累加算法 (Kahan 求和)
    fn kahan_sum(values: &[f64]) -> f64 {
        let mut sum = 0.0;
        let mut c = 0.0;  // 补偿值
        
        for &value in values {
            let y = value - c;
            let t = sum + y;
            c = (t - sum) - y;
            sum = t;
        }
        sum
    }
    
    let repeated = vec![0.1; 10000];
    let naive_sum: f64 = repeated.iter().sum();
    let kahan_sum = kahan_sum(&repeated);
    
    println!("\nKahan 求和算法:");
    println!("  朴素累加: {}", naive_sum);
    println!("  Kahan 累加: {}", kahan_sum);
    println!("  期望值: 1000.0");
    println!("  朴素误差: {}", (naive_sum - 1000.0).abs());
    println!("  Kahan 误差: {}", (kahan_sum - 1000.0).abs());
}

5.2 性能优化方法

浮点数运算的性能考虑。

rust 复制代码
fn performance_optimization() {
    println!("=== 性能优化 ===");
    
    use std::time::Instant;
    
    // 1. f32 与 f64 性能比较
    fn benchmark_operation<T>(name: &str, op: impl Fn(T) -> T, value: T, iterations: usize) 
    where T: Copy + std::fmt::Debug {
        let start = Instant::now();
        let mut result = value;
        for _ in 0..iterations {
            result = op(result);
        }
        let duration = start.elapsed();
        println!("  {}: {:?} ({:?})", name, result, duration);
    }
    
    println!("f32 vs f64 性能对比 (1000万次运算):");
    
    let f32_val = 3.14_f32;
    let f64_val = 3.14_f64;
    let iterations = 10_000_000;
    
    benchmark_operation("f32 乘法", |x| x * 1.000001, f32_val, iterations);
    benchmark_operation("f64 乘法", |x| x * 1.000001, f64_val, iterations);
    
    // 2. 避免重复计算
    println!("\n避免重复计算:");
    
    // 不好:重复计算
    fn inefficient(x: f64, y: f64) -> f64 {
        (x.sin() * y.cos()) + (x.sin() * y.sin())
    }
    
    // 好:缓存结果
    fn efficient(x: f64, y: f64) -> f64 {
        let sin_x = x.sin();
        sin_x * y.cos() + sin_x * y.sin()
    }
    
    // 3. 使用快速近似函数
    println!("\n快速近似 vs 精确计算:");
    
    fn fast_inv_sqrt(x: f32) -> f32 {
        // 快速反平方根近似 (来自 Quake III)
        let i = x.to_bits();
        let i = 0x5f3759df - (i >> 1);
        let y = f32::from_bits(i);
        y * (1.5 - 0.5 * x * y * y)
    }
    
    let test_value = 2.0;
    let fast = fast_inv_sqrt(test_value);
    let precise = 1.0 / test_value.sqrt();
    
    println!("  快速近似: 1/√{} ≈ {}", test_value, fast);
    println!("  精确计算: 1/√{} = {}", test_value, precise);
    println!("  相对误差: {:.2}%", (fast - precise).abs() / precise * 100.0);
    
    // 4. SIMD 优化提示
    println!("\nSIMD 优化:");
    println!("  考虑使用 packed_simd 或 std::simd crate");
    println!("  对大规模数值计算可大幅提升性能");
}

6. 浮点数比较策略

6.1 正确比较方法

浮点数不能直接使用 == 比较,需要特殊策略。

rust 复制代码
fn comparison_strategies() {
    println!("=== 浮点数比较策略 ===");
    
    // 1. 绝对误差比较(适用于已知尺度)
    fn abs_diff_eq(a: f64, b: f64, epsilon: f64) -> bool {
        (a - b).abs() < epsilon
    }
    
    // 2. 相对误差比较(适用于各种尺度)
    fn rel_diff_eq(a: f64, b: f64, epsilon: f64) -> bool {
        if a == b {
            return true;
        }
        let diff = (a - b).abs();
        let max = a.abs().max(b.abs());
        diff / max < epsilon
    }
    
    // 3. ULP(Units in Last Place)比较
    fn ulp_diff_eq(a: f32, b: f32, max_ulps: i32) -> bool {
        if a.is_nan() || b.is_nan() {
            return false;
        }
        if a.is_sign_positive() != b.is_sign_positive() {
            return a == b;  // +0 和 -0 相等
        }
        
        let a_bits = a.to_bits() as i32;
        let b_bits = b.to_bits() as i32;
        (a_bits - b_bits).abs() <= max_ulps
    }
    
    // 测试用例
    println!("比较示例:");
    
    let test_cases = [
        (1.0, 1.0000001, "相近的大数"),
        (0.0, 1e-10, "接近零"),
        (1e10, 1e10 + 1.0, "大数"),
        (f64::NAN, f64::NAN, "两个 NaN"),
    ];
    
    for (a, b, desc) in &test_cases {
        println!("\n{}:", desc);
        println!("  a = {}, b = {}", a, b);
        println!("  a == b: {}", a == b);
        println!("  abs_diff_eq (ε=1e-6): {}", abs_diff_eq(*a, *b, 1e-6));
        println!("  rel_diff_eq (ε=1e-6): {}", rel_diff_eq(*a, *b, 1e-6));
    }
    
    // 4. Rust 1.43+ 的近似比较方法
    println!("\nRust 标准库方法:");
    let x = 1.0_f64;
    let y = 1.0000001_f64;
    
    println!("  {}.total_cmp(&{}): {:?}", x, y, x.total_cmp(&y));
    
    // 5. 检查是否可排序
    let values = [3.14, f64::NAN, 2.71, f64::INFINITY];
    let mut sortable: Vec<f64> = values.iter().copied().filter(|&x| !x.is_nan()).collect();
    sortable.sort_by(|a, b| a.partial_cmp(b).unwrap());
    
    println!("\n可排序值: {:?}", sortable);
}

6.2 区间比较与排序

处理浮点数区间和排序问题。

rust 复制代码
fn range_comparison_and_sorting() {
    println!("=== 区间比较与排序 ===");
    
    // 1. 区间包含检查
    fn in_range(value: f64, min: f64, max: f64) -> bool {
        value >= min && value <= max
    }
    
    fn in_range_inclusive(value: f64, range: std::ops::RangeInclusive<f64>) -> bool {
        range.contains(&value)
    }
    
    println!("区间检查:");
    println!("  in_range(3.0, 1.0, 5.0) = {}", in_range(3.0, 1.0, 5.0));
    println!("  in_range(0.0, 1.0, 5.0) = {}", in_range(0.0, 1.0, 5.0));
    
    // 2. 浮点数排序策略
    let mut numbers = vec![3.14, 1.41, 2.71, f64::NAN, f64::INFINITY, f64::NEG_INFINITY];
    
    println!("\n原始数组: {:?}", numbers);
    
    // 分离 NaN 和有效数字
    let (nans, valid): (Vec<f64>, Vec<f64>) = 
        numbers.into_iter().partition(|&x| x.is_nan());
    
    // 对有效数字排序
    let mut sorted_valid = valid;
    sorted_valid.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
    
    // 重新组合(NaN 通常在最后)
    let mut final_sorted = sorted_valid;
    final_sorted.extend(nans);
    
    println!("排序后: {:?}", final_sorted);
    
    // 3. 自定义排序键(避免直接比较浮点数)
    #[derive(Debug)]
    struct Measurement {
        value: f64,
        uncertainty: f64,
    }
    
    let measurements = vec![
        Measurement { value: 3.14, uncertainty: 0.01 },
        Measurement { value: 2.71, uncertainty: 0.02 },
        Measurement { value: 1.41, uncertainty: 0.005 },
    ];
    
    println!("\n测量值按值排序:");
    let mut sorted_measurements = measurements;
    sorted_measurements.sort_by(|a, b| {
        a.value.partial_cmp(&b.value)
            .unwrap_or(std::cmp::Ordering::Equal)
    });
    
    for m in &sorted_measurements {
        println!("  {} ± {}", m.value, m.uncertainty);
    }
    
    // 4. 容差比较器
    struct ToleranceComparator {
        tolerance: f64,
    }
    
    impl ToleranceComparator {
        fn compare(&self, a: f64, b: f64) -> std::cmp::Ordering {
            if (a - b).abs() <= self.tolerance {
                std::cmp::Ordering::Equal
            } else if a < b {
                std::cmp::Ordering::Less
            } else {
                std::cmp::Ordering::Greater
            }
        }
    }
    
    let comparator = ToleranceComparator { tolerance: 0.01 };
    let values = [1.0, 1.005, 1.02, 1.03];
    
    println!("\n容差比较 (ε=0.01):");
    for i in 0..values.len() {
        for j in 0..values.len() {
            let ordering = comparator.compare(values[i], values[j]);
            println!("  {} vs {}: {:?}", values[i], values[j], ordering);
        }
    }
}

7. 实际应用场景

7.1 科学计算应用

浮点数在科学计算中的典型应用。

rust 复制代码
fn scientific_computing() {
    println!("=== 科学计算应用 ===");
    
    // 1. 数值积分(梯形法)
    fn integrate<F>(f: F, a: f64, b: f64, n: usize) -> f64 
    where F: Fn(f64) -> f64 {
        let h = (b - a) / n as f64;
        let mut sum = 0.5 * (f(a) + f(b));
        
        for i in 1..n {
            let x = a + i as f64 * h;
            sum += f(x);
        }
        
        sum * h
    }
    
    // 计算 π 的近似值(积分 ∫₀¹ 4/(1+x²) dx = π)
    let pi_approx = integrate(|x| 4.0 / (1.0 + x * x), 0.0, 1.0, 1000);
    println!("数值积分求 π:");
    println!("  近似值: {}", pi_approx);
    println!("  精确值: {}", std::f64::consts::PI);
    println!("  误差: {:.2e}", (pi_approx - std::f64::consts::PI).abs());
    
    // 2. 求解方程(二分法)
    fn bisect<F>(f: F, mut a: f64, mut b: f64, epsilon: f64) -> Option<f64>
    where F: Fn(f64) -> f64 {
        if f(a) * f(b) > 0.0 {
            return None; // 区间内无根
        }
        
        while (b - a).abs() > epsilon {
            let mid = (a + b) / 2.0;
            if f(mid) == 0.0 {
                return Some(mid);
            } else if f(a) * f(mid) < 0.0 {
                b = mid;
            } else {
                a = mid;
            }
        }
        
        Some((a + b) / 2.0)
    }
    
    // 求解 x³ - 2x - 5 = 0 在 [2, 3] 的根
    let root = bisect(|x| x.powi(3) - 2.0 * x - 5.0, 2.0, 3.0, 1e-10);
    println!("\n方程求解 (x³ - 2x - 5 = 0):");
    match root {
        Some(r) => println!("  根: {}", r),
        None => println!("  区间内无根"),
    }
    
    // 3. 统计分析
    fn statistics(data: &[f64]) -> (f64, f64, f64) {
        let n = data.len() as f64;
        let mean = data.iter().sum::<f64>() / n;
        
        let variance = data.iter()
            .map(|&x| (x - mean).powi(2))
            .sum::<f64>() / (n - 1.0);
        
        let std_dev = variance.sqrt();
        
        (mean, variance, std_dev)
    }
    
    let sample = [1.2, 2.3, 3.4, 4.5, 5.6];
    let (mean, variance, std_dev) = statistics(&sample);
    
    println!("\n统计分析:");
    println!("  数据: {:?}", sample);
    println!("  均值: {}", mean);
    println!("  方差: {}", variance);
    println!("  标准差: {}", std_dev);
    
    // 4. 物理模拟(简单弹簧振子)
    struct SpringOscillator {
        mass: f64,      // 质量 (kg)
        k: f64,         // 弹簧常数 (N/m)
        position: f64,  // 位置 (m)
        velocity: f64,  // 速度 (m/s)
    }
    
    impl SpringOscillator {
        fn new(mass: f64, k: f64, position: f64, velocity: f64) -> Self {
            SpringOscillator { mass, k, position, velocity }
        }
        
        fn update(&mut self, dt: f64) {
            // 简谐运动方程
            let acceleration = -self.k / self.mass * self.position;
            self.velocity += acceleration * dt;
            self.position += self.velocity * dt;
        }
    }
    
    let mut oscillator = SpringOscillator::new(1.0, 10.0, 0.5, 0.0);
    
    println!("\n弹簧振子模拟:");
    for i in 0..5 {
        oscillator.update(0.1);
        println!("  t={:.1}s: 位置={:.4}m, 速度={:.4}m/s", 
                 (i+1) as f64 * 0.1, oscillator.position, oscillator.velocity);
    }
}

7.2 图形与游戏开发

浮点数在图形和游戏中的关键应用。

rust 复制代码
fn graphics_and_gaming() {
    println!("=== 图形与游戏开发 ===");
    
    // 1. 向量和点运算
    #[derive(Debug, Clone, Copy)]
    struct Vector2 {
        x: f32,
        y: f32,
    }
    
    impl Vector2 {
        fn new(x: f32, y: f32) -> Self {
            Vector2 { x, y }
        }
        
        fn length(&self) -> f32 {
            (self.x * self.x + self.y * self.y).sqrt()
        }
        
        fn normalize(&self) -> Option<Vector2> {
            let len = self.length();
            if len > 0.0 {
                Some(Vector2 {
                    x: self.x / len,
                    y: self.y / len,
                })
            } else {
                None
            }
        }
        
        fn dot(&self, other: &Vector2) -> f32 {
            self.x * other.x + self.y * other.y
        }
        
        fn distance(&self, other: &Vector2) -> f32 {
            ((self.x - other.x).powi(2) + (self.y - other.y).powi(2)).sqrt()
        }
    }
    
    let v1 = Vector2::new(3.0, 4.0);
    let v2 = Vector2::new(1.0, 2.0);
    
    println!("向量运算:");
    println!("  v1 = {:?}", v1);
    println!("  长度: {}", v1.length());
    println!("  单位向量: {:?}", v1.normalize());
    println!("  点积 v1·v2: {}", v1.dot(&v2));
    println!("  距离: {}", v1.distance(&v2));
    
    // 2. 颜色表示(RGBA)
    #[derive(Debug)]
    struct Color {
        r: f32,  // 0.0 - 1.0
        g: f32,
        b: f32,
        a: f32,  // 透明度
    }
    
    impl Color {
        fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
            Color { r, g, b, a }
        }
        
        fn blend(&self, other: &Color) -> Color {
            let alpha = other.a;
            Color {
                r: self.r * (1.0 - alpha) + other.r * alpha,
                g: self.g * (1.0 - alpha) + other.g * alpha,
                b: self.b * (1.0 - alpha) + other.b * alpha,
                a: self.a.max(other.a),
            }
        }
        
        fn to_bytes(&self) -> [u8; 4] {
            [
                (self.r.clamp(0.0, 1.0) * 255.0) as u8,
                (self.g.clamp(0.0, 1.0) * 255.0) as u8,
                (self.b.clamp(0.0, 1.0) * 255.0) as u8,
                (self.a.clamp(0.0, 1.0) * 255.0) as u8,
            ]
        }
    }
    
    let red = Color::new(1.0, 0.0, 0.0, 1.0);
    let blue = Color::new(0.0, 0.0, 1.0, 0.5);
    let blended = red.blend(&blue);
    
    println!("\n颜色混合:");
    println!("  红色: {:?}", red.to_bytes());
    println!("  蓝色 (50%透明): {:?}", blue.to_bytes());
    println!("  混合后: {:?}", blended.to_bytes());
    
    // 3. 游戏物理(简单碰撞检测)
    struct Circle {
        center: Vector2,
        radius: f32,
    }
    
    impl Circle {
        fn collides_with(&self, other: &Circle) -> bool {
            let distance = self.center.distance(&other.center);
            distance <= (self.radius + other.radius)
        }
    }
    
    let circle1 = Circle {
        center: Vector2::new(0.0, 0.0),
        radius: 5.0,
    };
    
    let circle2 = Circle {
        center: Vector2::new(3.0, 4.0),
        radius: 3.0,
    };
    
    println!("\n碰撞检测:");
    println!("  圆1: 中心{:?}, 半径{}", circle1.center, circle1.radius);
    println!("  圆2: 中心{:?}, 半径{}", circle2.center, circle2.radius);
    println!("  距离: {}", circle1.center.distance(&circle2.center));
    println!("  是否碰撞: {}", circle1.collides_with(&circle2));
    
    // 4. 插值动画
    fn animate(start: f32, end: f32, duration: f32, t: f32) -> f32 {
        let progress = (t / duration).clamp(0.0, 1.0);
        // 缓动函数:平滑开始和结束
        let eased = 3.0 * progress.powi(2) - 2.0 * progress.powi(3);
        start + (end - start) * eased
    }
    
    println!("\n动画插值 (0.0 -> 100.0, 持续时间 2.0s):");
    for t in 0..=10 {
        let time = t as f32 * 0.2;
        let value = animate(0.0, 100.0, 2.0, time);
        println!("  t={:.1}s: value={:.2}", time, value);
    }
}

总结要点

Rust 浮点数的核心特性

  1. 两种类型 : f32(单精度)和 f64(双精度,默认)
  2. 标准遵循: 严格实现 IEEE 754-2008
  3. 性能平衡 : f64 在现代硬件上性能接近 f32 但精度更高

关键注意事项

  1. 精度问题: 0.1 在二进制中无法精确表示
  2. 比较陷阱 : 永远不要用 == 直接比较浮点数
  3. 特殊值: NaN、无穷大有特殊传播规则
  4. 舍入误差: 运算顺序影响最终结果

最佳实践

rust 复制代码
// ✅ 正确:使用容差比较
fn floats_equal(a: f64, b: f64) -> bool {
    (a - b).abs() < 1e-10
}

// ✅ 正确:处理特殊值
if value.is_nan() || value.is_infinite() {
    // 特殊处理
}

// ✅ 正确:使用标准数学函数
let result = x.sqrt().sin();

// ❌ 避免:直接相等比较
// if a == b { ... }  // 危险!

// ✅ 推荐:需要高精度时使用特殊算法
// 如 Kahan 求和、 compensated 算法等

选择指南

  • 科学计算 : 首选 f64,注意误差累积
  • 图形/游戏 : 考虑 f32 节省内存,注意精度
  • 嵌入式系统: 根据硬件能力选择
  • 金融计算: 考虑使用定点数或十进制库

性能提示

  1. 现代 CPU 上 f64 通常足够快
  2. 大量计算时考虑 SIMD 优化
  3. 避免在循环中重复计算相同表达式
  4. 使用融合乘加(FMA)指令提高精度和性能

Rust 的浮点数系统提供了强大而符合标准的数值计算能力,理解其特性和限制是编写可靠数值代码的关键。

相关推荐
明飞19871 天前
tauri
rust
咚为1 天前
Rust tokio:Task ≠ Thread:Tokio 调度模型中的“假并发”与真实代价
开发语言·后端·rust
天天进步20151 天前
Motia性能进阶与未来:从现有源码推测 Rust 重构之路
开发语言·重构·rust
Hello.Reader2 天前
Rocket 0.5 响应体系Responder、流式输出、WebSocket 与 uri! 类型安全 URI
websocket·网络协议·安全·rust·rocket
FreeBuf_2 天前
黑客利用React Native CLI漏洞(CVE-2025-11953)在公开披露前部署Rust恶意软件
react native·react.js·rust
鸿乃江边鸟2 天前
Spark Datafusion Comet 向量化Rust Native--Native算子(CometNativeExec)怎么串联执行
大数据·rust·spark·native
mit6.8242 天前
[]try catch no | result yes
rust
Ivanqhz2 天前
向量化计算
开发语言·c++·后端·算法·支持向量机·rust
mit6.8243 天前
rust等于C++的最佳实践
rust
初恋叫萱萱3 天前
基于 Rust 与 DeepSeek 构建高性能 Text-to-SQL 数据库代理服务
数据库·sql·rust