# 浮点数
数学中稠密的实数无法利用有限的计算机储存进行表示,出于精度和范围的平衡,目前世界上最广为使用的浮点数标准是 IEEE 754 标准
在该标准中,浮点数可以写为如下形式
其中,
-
表示数值正负,其中 取值为 或 ,分别表示正数和负数
-
表示数值有效数字,并且强调是 进制的数,通常来说 会固定为 或 ,而其他数 都是不到 的非负整数
-
表示数值的量级, 是一个整数,称为指数
-
单精度(Float):占用 4 字节,32 位,约 7 位十进制有效数字,在 C 语言中通过后缀
f/F来表示,例如0.5F -
双精度(Double):占用 8 字节,64 位,约 16 位十进制有效数字,在 C 语言中默认的浮点数类型,例如
0.5
在该标准中,一个 32 位的浮点数被划分为三个区域:
- 符号位 1 位(Sign, 1 bit):0 表示正数,1 表示负数
- 指数位 8 位(Exponent, 8 bits):采用偏移码(Biased representation)存储,决定了数值的量级范围。
- 尾数位 23 位(Fraction/Mantissa, 23 bits):存储有效数字。在规格化表示中,小数点前默认有一个隐藏的 “1”,因此实际有效位数为 24 位。
其数学表达式通常可抽象为:
关于浮点数,需要知道以下事实:
- 计算机中小数等价为 的负幂次方的和,因此
0.5F严格等于 ,同时0.1F略微大于 ,而0.7F略微小于 。 - 等同于整数的浮点数会被修正
参考以下 C 语言代码
#include <stdio.h> | |
int main() { | |
float a = 0.1F; | |
float b = 0.7F; | |
float c = 16777217.0F; | |
printf("a=%.10f\n", a); | |
printf("b=%.10f\n", b); | |
printf("c=%.10f\n", c); | |
} | |
// output: | |
// a=0.1000000015 | |
// b=0.6999999881 | |
// c=16777216.0000000000 |
该类错误被称为舍入误差「丸め誤差」,是由于浮点数的有限精度导致的。
舍入误差会导致运算的结合律不成立,例如 (0.1F + 0.3F) + 0.5F 与 0.1F + (0.3F + 0.5F) 的结果分别为 0.8999999762 和 0.9000000358
虽然我们知道 0.1F 略大于 ,但是如果将其累加十次,会变得比 更大,这种现象叫做信息丢失「情報落ち」,是在运算过程中,由于有效位数不足,小量被大数 “吞掉”,或者做差时前面很多位抵消,导致有用信息丢失。
#include <stdio.h> | |
int main() { | |
int i; | |
float x,s; | |
x=0.1; | |
s=0.0; | |
for(i=0;i<10;i++) s=s+x; | |
printf("s=%.10f\n", s); | |
} | |
// output: | |
// s=1.0000001192 |
关于浮点数的误差,还需要知道的是有效数字丢失「桁落ち」,这是两个非常接近的数相减时,前面许多相同的位被抵消掉,导致结果的有效数字大幅减少的现象。
让我们看以下二次方程求根代码示例
#include <stdio.h> | |
#include <math.h> | |
int main() { | |
float a,b,c,d,x1,x2; | |
a=2; b=12340.0; c=3; | |
d=b*b-4*a*c; | |
printf("a=%f b=%f c=%f d=%f\n", a, b, c, d); | |
x1=(-b+sqrt(d))/(2*a); | |
x2=(-b-sqrt(d))/(2*a); | |
printf("x1=%8f\n", x1); | |
printf("x2=%8f\n", x2); | |
} | |
// output: | |
// x1=-0.000162 | |
// x2=-6170.000000 |
然而,我们不妨代入计算可以得到
可以看出,虽然 的结果是正确的,但 的结果出现了大幅错误,这是因为分子两项的值过于接近
这种情况下应该使用 来计算 ,以避免有效数字丢失
# 浮点数的形式
在对浮点数的表现性质有了初步了解后,我们来了解一下浮点数的形式。
在计算机中,一个浮点数 可以表示为如下形式
其中,
- 表示数值正负,其中 取值为 或 ,分别表示正数和负数
- 表示数值有效数字,并且强调是 进制的数,通常来说 会固定为 或 ,而其他数 都是不到 的非负整数
- 表示数值的量级, 是一个整数,称为指数
也可以关注三个部分:符号部分 ,指数部分 与尾数部分
出于精度和范围的平衡,目前世界上最广为使用的浮点数标准是 IEEE 754 标准。我们将详细了解该标准
在该标准中指定进制为二进制,即 ,并且常见的有两种精度
- 单精度(Float):占用 4 字节(32 位),约 7 位十进制有效数字
- 在 C 语言中通过后缀
f/F来表示,例如0.5F。 - 该精度下的浮点数被划分为三个区域:符号位 1 位,指数位 8 位,尾数位 23 位
- 在 C 语言中通过后缀
- 双精度(Double):占用 8 字节(64 位),约 16 位十进制有效数字
- 这是 C 语言中默认的浮点数类型,例如
0.5。 - 该精度下的浮点数被划分为三个区域:符号位 1 位,指数位 11 位,尾数位 52 位
- 这是 C 语言中默认的浮点数类型,例如
在 IEEE 754 标准中,除了上述的正规化表示之外,还有一些特殊的表示形式
- 非数 NaN(Not a Number):用于表示未定义或不可表示的数值,例如 或 。
- 和 :分别表示正无穷和负无穷,用于表示超出可表示范围的数值,例如 。