浮点类型 Floating Point Numbers

与 C/C++ 一样, Rust 支持 IEEE 754 标准中要求的单精度 (single precision) 和双精度 (double precision) 浮点数, 分别是 f32 和 f64.

数据类型精度C 语言中的类型占用的字节数
f32单精度float4 字节
f64双精度double8 字节

浮点数值字面量 floating pointer literals

  • 使用 . 分隔整数位与小数位, 3.14159
  • 可以加上类型后缀, 如果忽略的话, 默认是 f64, 3.14_f32, 2.71828f32
  • 支持科学计数法的写法 1.2e4, 3.84e9

IEEE 754

IEEE 754 标准定义的浮点数, 在常见的编程语言中均有实现, 例如 c/c++/python/java/golang.

它规定了浮点数有三部分组成:

  • 符号位 (Sign), 占用 1 bit, 0 代表正数, 1 代表负数
  • 指数位 (Exponent), 指数部分,
  • 有效位 (Mantissa), 或者称作有效位数, 或者尾数. 它是浮点数具体数值的表示
(Sign) * Mantissa * 2 ^ (Exponent)
类型符号位 Sign指数位 Exponent有效位 Mantissa偏移量 Bias数值范围 Range
单精度1 (第31位)8 (30-23位)23 (22-0位)127 = 2^8-1-3.410^38 到 3.410^38
双精度1 (第63位)11 (62-52 位)52 (51-0位)1023 = 2^12-1-1.810^308 到 1.810^108

总之, 32 比特的单精度浮点数的内存如下图所示:

ieee754 single precision

64 比特的双精度浮点数的内存如下图所示:

ieee754 double precision

特殊的浮点数值:

数值符号位指数位有效位f32f64
00000.0_f320.0_f64
-01000.0_f320.0_f64
无穷大 (∞)02550f32::INFINITYf64::INFINITY
负无穷 (−∞)12550f32::NEG_INFINITYf64::NEG_INFINITY
非浮点数 (NAN)0255非 0f32::NANf64::NAN

浮点数常用的函数

  • sqrt(), cbrt() , 计算浮点数的二次方根和三次方根
  • powi(), powf(), 进行指数运算
  • exp(), exp2(), 分别以自然常数e和2为底做指数运算
  • log(), log2(), ln(), log10() 等, 做对数计算
  • sin(), cos(), tan() 等, 做三角函数计算
  • round(), ceil(), floor(), 浮点数四舍五入近似计算, 转为整数
  • is_finite(), is_infinite(), is_nan() 等判断特殊整数
  • is_sign_negative(), is_sign_positive(), signum() 等处理浮点的符号位
  • min(), max(), clamp() 用于限制浮点数的取值范围

比较两个浮点数是否相等

两个浮点数之间并不能直接比较它们是否相等, 也不能直接判断一个浮点数是不是等于 0.0, 因为浮点数不是精确的数, 浮点数并没有实现 Ord trait.

total_cmp() 方法可以缓解一部分限制, 它基于 IEEE 754 中的 totalOrder 规则, 该方法常用于浮点数的排序.

use std::cmp::Ordering;

fn main() {
    let a: f64 = 0.15 + 0.15 + 0.15;
    let b: f64 = 0.1 + 0.1 + 0.25;
    println!("{a} != {b}");
    // a 和 b 并不精确相等.
    assert_ne!(a, b);
    assert_eq!(a.total_cmp(&b), Ordering::Less);
}

有时还需要比较浮点数值之间是不是近似相等:

#[must_use]
#[inline]
pub fn nearly_equal_tolerance(p1: f64, p2: f64, tolerance: f64) -> bool {
    (p1 - p2).abs() < tolerance
}

#[must_use]
#[inline]
pub fn nearly_equal(p1: f64, p2: f64) -> bool {
    nearly_equal_tolerance(p1, p2, f64::EPSILON)
}

#[must_use]
#[inline]
pub fn nearly_is_zero(p: f64) -> bool {
    nearly_equal(p, 0.0)
}

fn main() {
    let a: f64 = 0.15 + 0.15 + 0.15;
    let b: f64 = 0.1 + 0.1 + 0.25;
    println!("{a} != {b}");
    // a 和 b 并不精确相等.
    assert_ne!(a, b);
    assert!(nearly_equal(a, b));
    assert!(!nearly_equal(f64::INFINITY, f64::INFINITY));
    assert!(!nearly_equal(f64::NAN, f64::INFINITY));
}

或者使用第三方库 float_cmp.

参考