在学习如何在计算机中表示浮点数时,我遇到了“偏见值”一词,我不太明白。
浮点数的偏差值与浮点数的指数部分的负和正有关。
浮点数的偏差值为127,这意味着127总是被添加到浮点数的指数部分。这样做有助于确定指数是负还是正?
答案 0 :(得分:64)
b0lt已经解释了偏见是如何运作的。猜测一下,也许你想知道为什么他们在这里使用偏见表示,即使几乎所有的现代计算机都使用其他地方的两个补码(甚至是不使用二进制补码的机器,使用一个补码或符号幅度,而不是偏见)。
IEEE浮点标准的目标之一是您可以将浮点数的位视为相同大小的(带符号)整数,如果您将它们进行比较,则值将分类为与它们代表的浮点数相同的顺序。
如果对指数使用二进制补码表示,则小的正数(即带负指数)看起来像大整数,因为第二个MSB将被设置。相反,通过使用偏差表示法,您不会遇到这种情况 - 浮点数中较小的指数总是看起来像一个较小的整数。
FWIW,这也是为什么浮点数通常首先用符号排列,然后是指数,最后是最低有效位中的有效数 - 这样,你可以采用正浮点数,将这些位视为整数,并对它们进行排序。执行此操作时,结果将以正确的顺序具有浮点数。例如:
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
// some arbitrary floating point values
std::vector<double> vals = { 1e21, 1, 2.2, 2, 123, 1.1, 0.0001, 3, 17 };
std::vector<long long> ivals;
// Take those floating point values, and treat the bits as integers:
for (auto &&v : vals)
ivals.push_back(*reinterpret_cast<long long *>(&v));
// Sort them as integers:
std::sort(ivals.begin(), ivals.end());
// Print out both the integers and the floating point value those bits represent:
for (auto &&i : ivals)
std::cout << i << "\t(" << *reinterpret_cast<double *>(&i) << ")\n";
}
当我们运行它时,结果如下所示:
4547007122018943789 (0.0001)
4607182418800017408 (1)
4607632778762754458 (1.1)
4611686018427387904 (2)
4612136378390124954 (2.2)
4613937818241073152 (3)
4625478292286210048 (17)
4638355772470722560 (123)
4921056587992461136 (1e+21)
正如您所看到的,即使我们将它们整理为整数,这些位代表的浮点数也会以正确的顺序出现。
这对浮点数有限制。虽然所有(非古代)计算机都同意正数的表示,但有三种表示(最近)用于有符号数:有符号幅度,一个补码和二进制补码。
将这些位视为整数并进行比较可以在使用带符号幅度表示整数的计算机上正常工作。对于使用一个补码或两个补码的计算机,负数将按倒序排序。由于这仍然是一个简单的规则,因此编写适合它的代码非常容易。如果我们将上面的sort
调用更改为以下内容:
std::sort(ivals.begin(), ivals.end(),
[](auto a, auto b) { if (a < 0.0 && b < 0.0) return b < a; return a < b; }
);
...然后它会正确地对正数和负数进行排序。例如,输入:
std::vector<double> vals = { 1e21, 1, 2.2, 2, 123, 1.1, 0.0001, 3, 17, -0.001, -0.00101, -1e22 };
会产生以下结果:
-4287162073302051438 (-1e+22)
-4661071411077222194 (-0.00101)
-4661117527937406468 (-0.001)
4547007122018943789 (0.0001)
4607182418800017408 (1)
4607632778762754458 (1.1)
4611686018427387904 (2)
4612136378390124954 (2.2)
4613937818241073152 (3)
4625478292286210048 (17)
4638355772470722560 (123)
4921056587992461136 (1e+21)
答案 1 :(得分:53)
在单精度浮点数中,您可以获得8位来存储指数。不是将它存储为带符号的二进制补码数,而是决定向指数添加127更容易(因为8位签名的最低值为-127)并且只是将其存储为无符号数。如果存储的值大于偏差,则意味着指数的值为正,如果它低于偏差,则为负,如果等于,则为零。
答案 2 :(得分:15)
在上述答案中添加更多细节。
要以浮点表示0
,infinity
和NaN
(非数字),IEEE决定使用特殊编码值。
如果指数字段的所有位都设置为0,则浮点数为0.0。
如果指数字段的所有位都设置为1且小数部分的所有位都为0,则浮点数为无穷大。
如果指数字段的所有位都设置为1且小数部分的所有位都不等于0,则浮点数为 NaN 。
< / LI>因此,在单精度中,我们有8位来表示指数字段,并且有2个特殊值,因此我们基本上有256 - 2 = 254
个值可以用exponent表示。因此,我们可以在指数中有效地表示-126到127,即254个值(126 + 127 + 1),1加0表示。
答案 3 :(得分:1)
要专门解决您的困惑:由于偏见,指数似乎会变为负数。如果在指数范围内看到二进制值+125,则将其“无偏”后,实际指数值为-2。之所以会发生这种情况,是因为在这种情况下被“偏置”意味着要减去127。即使在减去127之后,指数仍会保持正值。这是有一段时间的。如果您只是看一些位:
[0][01111111][00000000000000000000000]
是数字零!即使您在那里看到所有这些1。这些示例适用于使用IEEE 754标准的处理器的单精度(32位)浮点数。使用此标准值时,将按以下方式存储:
[sign][biased exponent][significand]
SIGN位用于浮点数的小数部分,而不是指数。您的眼睛必须移动符号位和指数,以使这些数字看起来更自然,例如1.01x2 ^ 5,就像在数学课中看到的那样。顺便说一句,1.01x2 ^ 5被认为是一个“正常”数字,因为二进制点的左边只有1位数字,当然,此科学计数法的版本乘以2而不是10,因为我们使用的是基数2,因此轻松移动二进制点!
让我们看一个如十进制0.15625的示例,首先,我将在视觉上移动指数:
----------------------------------(exponent)
0 01111100 01000000000000000000000 ^
--+------+-+----------------------- |
| | |
+------+ |
subtract 127 here |
| |
v |
---------------->--------------
此处的指数为124,因此减去127可获得-3。现在记住隐含的1,这样您现在将拥有1.01000000000000000000000。忘记所有这些零:1.01x2 ^ -3是二进制数0.001010。还要记住,第一位是零,因此“最终”数是正0.15625。如果我们以1 01111100 01000000000000000000000
开头,则很容易得到-0.15625。
这是上面提到的特殊情况,是的,存在正负无穷大:
31
|
| 30 23 22 0
| | | | |
-----+-+------+-+---------------------+
qnan 0 11111111 10000000000000000000000
snan 0 11111111 01000000000000000000000
inf 0 11111111 00000000000000000000000
-inf 1 11111111 00000000000000000000000
-----+-+------+-+---------------------+
| | | | |
| +------+ +---------------------+
| | |
| v v
| exponent fraction
|
v
sign
我在《英特尔手册》第4卷第91页表4-3中找到了所有这些内容。