Binary 浮点精度是可变的还是不变的?

Binary 浮点精度是可变的还是不变的?,binary,floating-point,decimal,floating-point-precision,significant-digits,Binary,Floating Point,Decimal,Floating Point Precision,Significant Digits,对于浮点数(即float、double、或long double)是否有一个且只有一个精度值,或者是否有一个可以变化的精度值,我的答案总是褒贬不一 一个被称为的主题似乎暗示浮点精度是绝对的 但是另一个话题叫, 通常,双精度的小数位数为15到16 另一个说, 浮点型变量的精度通常为约7位有效数字 double类型变量的精度通常为约16位有效数字 如果我使用的是敏感代码,当我的值不精确时,这些代码很容易被破坏,我不喜欢参考上面的近似值。所以,让我们澄清一下。浮点精度是可变的还是不变的,原因是什么?精

对于浮点数(即
float
double
、或
long double
)是否有一个且只有一个精度值,或者是否有一个可以变化的精度值,我的答案总是褒贬不一

一个被称为的主题似乎暗示浮点精度是绝对的

但是另一个话题叫,

通常,双精度的小数位数为15到16

另一个说,

浮点型变量的精度通常为7位有效数字

double类型变量的精度通常为16位有效数字


如果我使用的是敏感代码,当我的值不精确时,这些代码很容易被破坏,我不喜欢参考上面的近似值。所以,让我们澄清一下。浮点精度是可变的还是不变的,原因是什么?

精度是固定的,对于双精度,精确到53位二进制数字(或者52位,如果我们排除隐式前导1)。这大约是15位十进制数字


OP让我详细解释一下为什么正好有53个二进制数字意味着“大约”15个十进制数字

为了直观地理解这一点,让我们考虑一个不太精确的浮点格式:代替一个52位尾数的双精度数字,我们只需要使用一个4位尾数。 因此,每个数字看起来像:(-1)s×2yyy×1.xxxx(其中

s
是符号位,
yyy
是指数,
1.xxxx
是标准化尾数)。对于当前的讨论,我们将只关注尾数,而不是符号或指数

下面是一个表格,列出了所有
xxxx
值的
1.xxxx
外观(所有舍入都是一半到偶数,就像默认的浮点舍入模式一样):

xxxx | 1.xxxx |值| 2dd | 3dd
--------+----------+----------+-------+--------
0000  |  1.0000  |  1.0     |  1.0  |  1.00
0001  |  1.0001  |  1.0625  |  1.1  |  1.06
0010  |  1.0010  |  1.125   |  1.1  |  1.12
0011  |  1.0011  |  1.1875  |  1.2  |  1.19
0100  |  1.0100  |  1.25    |  1.2  |  1.25
0101  |  1.0101  |  1.3125  |  1.3  |  1.31
0110  |  1.0110  |  1.375   |  1.4  |  1.38
0111  |  1.0111  |  1.4375  |  1.4  |  1.44
1000  |  1.1000  |  1.5     |  1.5  |  1.50
1001  |  1.1001  |  1.5625  |  1.6  |  1.56
1010  |  1.1010  |  1.625   |  1.6  |  1.62
1011  |  1.1011  |  1.6875  |  1.7  |  1.69
1100  |  1.1100  |  1.75    |  1.8  |  1.75
1101  |  1.1101  |  1.8125  |  1.8  |  1.81
1110  |  1.1110  |  1.875   |  1.9  |  1.88
1111  |  1.1111  |  1.9375  |  1.9  |  1.94
你说它提供了多少个十进制数字?你可以说是2,因为两个十进制数字范围内的每个值都被覆盖,尽管不是唯一的;也可以说3,它覆盖了所有唯一的值,但不覆盖三位小数范围内的所有值

为了便于论证,我们将说它有两个十进制数字:十进制精度将是那些十进制数字的所有值都可以表示的位数


好吧,那么,如果我们将所有数字减半(因此我们使用的是
yyy
=-1),会发生什么呢

xxxx | 1.xxxx |值| 1dd | 2dd
--------+----------+-----------+-------+--------
0000  |  1.0000  |  0.5      |  0.5  |  0.50
0001  |  1.0001  |  0.53125  |  0.5  |  0.53
0010  |  1.0010  |  0.5625   |  0.6  |  0.56
0011  |  1.0011  |  0.59375  |  0.6  |  0.59
0100  |  1.0100  |  0.625    |  0.6  |  0.62
0101  |  1.0101  |  0.65625  |  0.7  |  0.66
0110  |  1.0110  |  0.6875   |  0.7  |  0.69
0111  |  1.0111  |  0.71875  |  0.7  |  0.72
1000  |  1.1000  |  0.75     |  0.8  |  0.75
1001  |  1.1001  |  0.78125  |  0.8  |  0.78
1010  |  1.1010  |  0.8125   |  0.8  |  0.81
1011  |  1.1011  |  0.84375  |  0.8  |  0.84
1100  |  1.1100  |  0.875    |  0.9  |  0.88
1101  |  1.1101  |  0.90625  |  0.9  |  0.91
1110  |  1.1110  |  0.9375   |  0.9  |  0.94
1111  |  1.1111  |  0.96875  |  1.   |  0.97
按照与以前相同的标准,我们现在处理的是1位小数。因此,您可以看到,根据指数的不同,您可以有更多或更少的十进制数字,因为二进制和十进制浮点数之间没有清晰的映射


同样的论点也适用于双精度浮点数(尾数为52位),只有在这种情况下,根据指数,你才能得到15或16位十进制数字。

答案很简单,但很复杂。这些数字以二进制形式存储。根据它是浮点还是双精度,计算机使用不同数量的二进制来存储数字。您得到的精度取决于二进制文件。如果你不知道二进制数是如何工作的,最好查一查。但简单地说,有些数字比其他数字需要更多的1和0


因此,精度是固定的,但实际精度取决于使用的数字。

浮点变量的类型定义了值的范围以及可以表示的小数位数(!)。由于十进制分数和二进制分数之间没有整数关系,因此十进制分数实际上是一个近似值

第二:另一个问题是执行精确的算术运算。想想
1.0/3.0
或PI。这样的值不能用有限的数字来表示——既不是十进制的,也不是二进制的。因此,这些值必须四舍五入以适应给定的空间。小数位数越多,精度越高

现在考虑应用多个这样的操作,例如PI/3.0。这需要四舍五入两次:PI a
/*
1 234567890123456789 */
1.000000000000000000...
1.000000000000000222...
/*
1 234567890123456789 */
8.521812787393891
8.521812787393891142073699...
8.521812787393892
void Kaboom( float a, float b, float c ) // same is true for other floating point types.
{
    float sum1 = a+b+c;
    float sum2 = a+b;
    sum2 += c; // let's assume that the compiler did not keep sum2 in a register and the value was write to memory then load again.
    if (sum1 !=sum2)
        throw "kaboom"; // this can happen.
}