1. 浮点数类型概述
1.1 两种浮点类型
Rust 提供两种符合 IEEE 754 标准的浮点数类型:
| 类型 | 位宽 | 精度 | 范围(近似) | 默认 |
|---|---|---|---|---|
f32 |
32位 | 单精度 | ±3.4×10³⁸ | 否 |
f64 |
64位 | 双精度 | ±1.8×10³⁰⁸ | 是 |
为什么默认是 f64?
在现代 CPU 上,f64 与 f32 性能相近,但提供更高精度和更大范围。
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 浮点数的核心特性
- 两种类型 :
f32(单精度)和f64(双精度,默认) - 标准遵循: 严格实现 IEEE 754-2008
- 性能平衡 :
f64在现代硬件上性能接近f32但精度更高
关键注意事项
- 精度问题: 0.1 在二进制中无法精确表示
- 比较陷阱 : 永远不要用
==直接比较浮点数 - 特殊值: NaN、无穷大有特殊传播规则
- 舍入误差: 运算顺序影响最终结果
最佳实践
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节省内存,注意精度 - 嵌入式系统: 根据硬件能力选择
- 金融计算: 考虑使用定点数或十进制库
性能提示
- 现代 CPU 上
f64通常足够快 - 大量计算时考虑 SIMD 优化
- 避免在循环中重复计算相同表达式
- 使用融合乘加(FMA)指令提高精度和性能
Rust 的浮点数系统提供了强大而符合标准的数值计算能力,理解其特性和限制是编写可靠数值代码的关键。