一、计算机存储浮点数的算法
在计算机中,浮点数通常采用IEEE 754标准进行存储,该标准定义了两种主要的浮点数格式:单精度浮点数(32位)和双精度浮点数(64位)。
(一)单精度浮点数
- 占用32位存储空间。
- 格式分为三个部分:
- 符号位(1位):0表示正数,1表示负数。
- 指数位(8位):用于表示浮点数的指数大小。
- 尾数位(23位):表示浮点数的小数部分。
其存储的值计算公式为 ( − 1 ) s i g n × 1. f r a c t i o n × 2 e x p o n e n t − b i a s (-1)^{sign}×1.fraction×2^{exponent - bias} (−1)sign×1.fraction×2exponent−bias(其中 s i g n sign sign是符号位, f r a c t i o n fraction fraction是尾数位表示的小数部分, e x p o n e n t exponent exponent是指数位的值,对于单精度浮点数, b i a s bias bias为127)。
(二)双精度浮点数
- 占用64位存储空间。
- 格式同样分为符号位(1位)、指数位(11位)和尾数位(52位)。
二、为什么浮点数不能精确存储
(一)有限的位数
无论是单精度还是双精度浮点数,位数有限。在表示一些无限小数或者非常大/非常小的数时,必然存在精度损失。例如,十进制的 0.1 0.1 0.1在二进制中是无限循环小数 0.00011001100110011 … … 0.00011001100110011…… 0.00011001100110011……,由于浮点数位数有限,无法精确表示,只能近似存储。
(二)舍入误差
在将十进制数转换为二进制浮点数存储以及进行浮点数运算时,都可能发生舍入误差。例如, 0.2 0.2 0.2和 0.3 0.3 0.3相加,十进制结果为 0.5 0.5 0.5,但在计算机中,因浮点数精度限制, 0.2 0.2 0.2和 0.3 0.3 0.3不能精确表示,相加结果与 0.5 0.5 0.5有微小差异。
三、如何判断两个浮点数相等
由于浮点数存在精度问题,不能直接用==
判断。通常采用以下方法:
(一)设置误差范围
- 定义一个非常小的数作为误差范围,如 ϵ = 1 e − 6 \epsilon = 1e - 6 ϵ=1e−6。
- 判断两个浮点数之差的绝对值是否小于 ϵ \epsilon ϵ,若是,则认为两浮点数相等。例如:
#include <stdio.h>
#include <math.h>
// 设置误差范围判断两个浮点数是否相等
void compareFloatsWithEpsilon() {
double epsilon = 1e-6;
double a = 0.1 + 0.2;
double b = 0.3;
if (fabs(a - b) < epsilon) {
printf("a 和 b 相等\n");
} else {
printf("a 和 b 不相等\n");
}
}
(二)相对误差判断
- 计算两个浮点数的相对误差,即两数之差除以其中一个数的绝对值。
- 如果相对误差小于给定阈值,则认为两浮点数相等。例如:
// 通过相对误差判断两个浮点数是否相等
void compareFloatsWithRelativeError() {
double threshold = 1e-6;
double a = 0.1 + 0.2;
double b = 0.3;
double relative_error = fabs(a - b) / (fabs(a) > fabs(b)? fabs(a) : fabs(b));
if (relative_error < threshold) {
printf("a 和 b 相等\n");
} else {
printf("a 和 b 不相等\n");
}
}