浮点数
“浮点”指一组对实数进行编码的数据类型,包括分数和小数。浮点数据类型允许小数点后有不同位数,而定点数据类型在小数点前后则保留特定位数。因此与定点数据类型相比,浮点数据类型可以表示更广范围的数值。
由于计算机在数字表示和存储方面的内存有限,只能表示有限精度的浮点数有限集合。这种有限精度无法精确表示某些数值,因而会限制需要精确值或高精度的浮点计算的准确度。尽管浮点数有其局限性,但其计算速度快且精度和范围足以求解现实世界的很多问题,因此得到广泛使用。
MATLAB 中的浮点数
MATLAB® 提供遵循 IEEE® 标准 754 的双精度 (double
) 和单精度 (single
) 浮点数数据类型。默认情况下,MATLAB 以双精度表示浮点数。双精度支持以更高的精度表示数值,但比单精度需要更多内存。为了节省内存,可以使用 single
函数将数值转换为单精度。
如果数值范围约在 –3.4 × 1038 和 3.4 × 1038 之间,您既可以用单精度,也可以用双精度来存储。如果数值超出该范围,请用双精度来存储。
创建双精度数据
由于 MATLAB 的默认数值类型是 double
类型,因此可以用简单的赋值语句创建双精度浮点数。
x = 10; c = class(x)
c = 'double'
您可以使用 double
函数将数值数据、字符或字符串以及逻辑数据转换为双精度值。例如,将有符号整数转换为双精度浮点数。
x = int8(-113); y = double(x)
y = -113
创建单精度数据
要创建单精度数值,请使用 single
函数。
x = single(25.783);
还可以使用 single
函数将数值数据、字符或字符串以及逻辑数据转换为单精度。例如,将有符号整数转换为单精度浮点数。
x = int8(-113); y = single(x)
y = single -113
MATLAB 如何存储浮点数
默认情况下,MATLAB 根据 IEEE 格式构造其 double
和 single
浮点数据类型,并遵循就近舍入,偶数优先舍入模式。
浮点数 x 的形式如下:
其中:
s 确定符号。
f 是满足 0 ≤f< 1 的小数(或尾数)。
e 是指数。
s、f 和 e 分别由内存中有限数量的位确定,其中 f 和 e 取决于数据类型的精度。
存储 double
数需要 64 位,如下表所示。
位 | 宽度 | 用法 |
---|---|---|
63 | 1 | 存储符号,其中 0 表示正值,1 表示负值 |
62 到 52 | 11 | 存储指数,偏移量为 1023 |
51 到 0 | 52 | 存储尾数 |
存储 single
数需要 32 位,如下表所示。
位 | 宽度 | 用法 |
---|---|---|
31 | 1 | 存储符号,其中 0 表示正值,1 表示负值 |
30 到 23 | 8 | 存储指数,偏移量为 127 |
22 到 0 | 23 | 存储尾数 |
浮点数据类型的最大值和最小值
双精度和单精度数据类型各有其可表示的最大值和最小值。超出可表示范围的数值被视为正无穷或负无穷。但是,由于连续浮点数之间存在间隔并且有些数值可能存在舍入误差,可表示范围内的某些数值无法精确存储。
最大和最小双精度值
分别使用 realmax
和 realmin
函数,找出可以用 double
数据类型表示的最大和最小正值。
m = realmax
m = 1.7977e+308
n = realmin
n = 2.2251e-308
realmax
和 realmin
返回规范化的 IEEE 值。将 realmax
和 realmin
乘以 -1
可求出最大和最小负值。大于 realmax
或小于 –realmax
的数值分别被视为正无穷值或负无穷值。
最大和最小单精度值
通过使用参量 "single"
调用 realmax
和 realmin
函数,求出可以用 single
数据类型表示的最大和最小正值。
m = realmax("single")
m = single 3.4028e+38
n = realmin("single")
n = single 1.1755e-38
将 realmax("single")
和 realmin("single")
乘以 –1
可求出最大和最小负值。大于 realmax("single")
或小于 –realmax("single")
的数值分别被视为正无穷值或负无穷值。
最大连续浮点整数
并非所有整数都可以用浮点数据类型表示。最大连续整数 x 是能精确表示的最大整数,所有小于或等于 x 的整数都能精确表示,但 x + 1 无法以浮点格式表示。flintmax
函数返回最大连续整数。例如,使用 flintmax
函数求出双精度浮点格式的最大连续整数,即 253。
x = flintmax
x = 9.0072e+15
求出单精度浮点格式的最大连续整数,即 224。
y = flintmax("single")
y = single 16777216
当您将整数数据类型转换为浮点数据类型时,无法用浮点格式精确表示的整数会损失精度。flintmax
是浮点数,它小于使用相同位数的整数数据类型所能表示的最大整数。例如,双精度的 flintmax
是 253,而 int64
类型的最大值是 264 - 1。因此,将大于 253 的整数转换为双精度会导致精度损失。
浮点数据的精度
浮点数据的精度受下面几个因素的影响:
计算机硬件的限制 - 例如,内存不足的硬件会截断浮点计算的结果。
每个浮点数与下一个更大的浮点数之间的间隔 - 这些间隔存在于任何计算机上,并会限制精度。
浮点数之间的间隔
您可以使用 eps
函数来确定连续浮点数之间的间隔大小。例如,计算 5
和下一个更大的双精度数之间的间距。
e = eps(5)
e = 8.8818e-16
您无法以双精度格式表示 5
和 5 + eps(5)
之间的数值。如果双精度计算返回答案 5
,则该结果精确到 eps(5)
范围内。此精度半径通常称为机器精度。
各浮点数之间的间隔并不相等。例如,1e10
和下一个更大的双精度数之间的间隔大于 5
和下一个更大的双精度数之间的间隔。
e = eps(1e10)
e = 1.9073e-06
同样,求 5
和下一个更大的单精度数之间的间距。
x = single(5); e = eps(x)
e = single 4.7684e-07
单精度数之间的间隔大于双精度数之间的间隔,因为单精度数的数量较少。因此,单精度计算的结果不如双精度计算的结果精确。
当您将双精度数转换为单精度数时,可以使用 eps
函数来确定该数值舍入量的上界。例如,当您将双精度数 3.14
转换为单精度数时,该数值的最大舍入量为 eps(single(3.14))
。
连续浮点整数之间的间隔
flintmax
函数以浮点格式返回最大连续整数。高于此值时,连续浮点整数的间隔大于 1
。
使用 eps
求出 flintmax
和下一个浮点数之间的间隔:
format long
x = flintmax
x = 9.007199254740992e+15
e = eps(x)
e = 2
由于 eps(x)
为 2
,因此可以精确表示的下一个更大的浮点数为 x + 2
。
y = x + e
y = 9.007199254740994e+15
如果将 1
与 x
相加,结果将舍入到 x
。
z = x + 1
z = 9.007199254740992e+15
浮点数的算术运算
您可以在浮点数的算术运算中使用一系列数据类型,结果的数据类型取决于输入类型。但是,当您使用不同数据类型执行运算时,由于逼近或中间转换,某些计算可能不精确。
双精度操作数
您可以使用 double
和以下的任何其他数据类型来执行基本算术运算。如果一个或多个操作数为整数标量或数组,则 double
操作数必须为标量。运算结果默认为 double
类型,除非另有说明。
single
- 结果为single
类型。double
int8
、int16
、int32
、int64
- 结果的数据类型与整数操作数的数据类型相同。uint8
、uint16
、uint32
、uint64
- 结果的数据类型与整数操作数的数据类型相同。char
logical
单精度操作数
您可以使用 single
和以下的任何其他数据类型来执行基本算术运算。结果为 single
类型。
single
double
char
logical
浮点算术的意外结果
MATLAB 中几乎所有运算都是在符合 IEEE 标准 754 的双精度算术中执行的。由于计算机以有限精度表示数值,因此一些计算会产生在数学上不直观的结果。使用浮点数进行计算时可能出现的一些常见问题是舍入误差、抵消、淹没和中间转换。意外的结果并非 MATLAB 中的 Bug,任何使用浮点数的软件都会出现这种情况。如果需要数值的精确有理表示形式,请考虑使用 Symbolic Math Toolbox™。
舍入误差
浮点数的有限精度表示可能导致舍入误差。例如,数值 4/3
不能精确表示为二进制分数。因此,下面的计算会返回数量 eps(1)
,而不是 0
。
e = 1 - 3*(4/3 - 1)
e = 2.2204e-16
同样,由于 pi
不是 π 的精确表示,sin(pi)
也不精确为零。
x = sin(pi)
x = 1.2246e-16
当对浮点数执行许多运算时,误差会不断累积和复合,舍入误差最为显著。最佳做法是尽可能减少运算次数。
抵消
根据 eps
的测量,当您从数量级大致相同的一个数中减去另一个数时,可能发生抵消。例如,eps(2^53)
为 2
,因此数值 2^53 + 1
和 2^53
具有相同的浮点表示。
x = (2^53 + 1) - 2^53
x = 0
请尽可能尝试用一种等效形式重写计算,以避免发生抵消。
淹没
当您对数量级相差特别大的浮点数执行运算时,可能发生淹没。例如,下面的计算显示精度损失会使加法运算失去意义。
x = 1 + 1e-16
x = 1
中间转换
当您使用不同数据类型执行算术时,中间计算和转换可能会产生意外的结果。例如,虽然 x
和 y
均为 0.2
,但将它们相减会产生非零结果。原因是在执行减法之前 y
先转换为 double
类型。然后此减法的结果再转换为 single
类型的 z
。
format long
x = 0.2
x = 0.200000000000000
y = single(0.2)
y = single 0.2000000
z = x - y
z = single -2.9802323e-09
线性代数
浮点算术中的常见问题(如上述问题)在应用于线性代数问题时会复合,因为相关计算通常由多个步骤组成。例如,在求解线性方程组 Ax = b
时,MATLAB 警告结果可能不准确,因为操作数矩阵 A
为病态矩阵。
A = diag([2 eps]); b = [2; eps]; x = A\b;
Warning: Matrix is close to singular or badly scaled. Results may be inaccurate. RCOND = 1.110223e-16.
参考
[1] Moler, Cleve. Numerical Computing with MATLAB. Natick, MA: The MathWorks, Inc., 2004.