浮点类型 Floating Point Numbers
与 C/C++ 一样, Rust 支持 IEEE 754 标准中要求的单精度 (single precision) 和双精度 (double precision) 浮点数, 分别是 f32 和 f64.
数据类型 | 精度 | C 语言中的类型 | 占用的字节数 |
---|---|---|---|
f32 | 单精度 | float | 4 字节 |
f64 | 双精度 | double | 8 字节 |
浮点数值字面量 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 比特的单精度浮点数的内存如下图所示:
64 比特的双精度浮点数的内存如下图所示:
特殊的浮点数值:
数值 | 符号位 | 指数位 | 有效位 | f32 | f64 |
---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0.0_f32 | 0.0_f64 |
-0 | 1 | 0 | 0 | 0.0_f32 | 0.0_f64 |
无穷大 (∞) | 0 | 255 | 0 | f32::INFINITY | f64::INFINITY |
负无穷 (−∞) | 1 | 255 | 0 | f32::NEG_INFINITY | f64::NEG_INFINITY |
非浮点数 (NAN) | 0 | 255 | 非 0 | f32::NAN | f64::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.