为什么double可以存储比无符号long long更大的数字?

时间:2015-05-05 12:16:12

标签: c++ c floating-point precision floating-point-conversion

问题是,我不太明白为什么double可以存储比无符号long long更多的数字。由于它们都是8字节长,所以64位。

在无符号long long中,所有64位用于存储值,另一方面double表示1表示符号,11表示指数,52表示尾数。即使用于尾数的52位将用于存储没有浮点的十进制数,它仍然有63位......

但LLONG_MAX明显小于DBL_MAX ......

为什么?

6 个答案:

答案 0 :(得分:25)

原因是unsigned long long将存储精确整数,而double存储尾数(具有有限的52位精度)和指数。

这允许double存储非常大的数字(大约10 308 ),但不完全相同。 double中有大约15个(差不多16个)有效十进制数字,其余308个可能的小数是零(实际上是未定义的,但为了更好地理解,你可以假设为“零”)。
unsigned long long只有19个数字,但每个数字都是精确定义的。

修改
在回复以下评论“这是如何工作”时,符号为1位,指数为11位,尾数为52位。尾数在开头有一个隐含的“1”位,没有存储,所以有效地你有53个尾数位。 2 53 是9.007E15,因此您有15个,几乎16个十进制数字可供使用。
指数有一个符号位,范围从-1022到+1023,用于缩放(二进制左移或右移)尾数(2 1023 大约10 307 ,因此限制范围),因此这种格式同样可以使用非常小和非常大的数字 但是,当然,您可以表示的所有数字都具有与matissa相同的精度。

总而言之,浮点数不是很直观,因为“简单”十进制数不一定表示为浮点数。这是因为尾数是二进制的。例如,可以(并且很容易)表示任何高达几十亿的正整数,或者数字,如0.5或0.25或0.0125,具有完美的精度。
另一方面,也可以表示像10 250 这样的数字,但仅约为。事实上,你会发现10 250 和10 250 +1是相同的数字(等待,什么???)。这是因为虽然您可以轻松拥有250个数字,但您没有那么多重要数字(将“重要”视为“已知”或“已定义”)。
此外,表示像0.3这样看似简单的东西只能近似,即使0.3甚至不是“大”数字。但是,你不能用二进制表示0.3,并且无论你附加什么二进制指数,你都不会找到任何导致精确为0.3的二进制数(但你可以非常接近)。

某些“特殊值”保留用于“无穷大”(正面和负面)以及“非数字”,因此非常轻微小于理论范围。

另一方面,

unsigned long long不会以任何方式解释位模式。您可以表示的所有数字都只是位模式表示的确切数字。每个数字的每个数字都是精确定义的,不会发生缩放。

答案 1 :(得分:13)

IEEE754浮点值可以存储更大的范围数字,因为它们牺牲了精度。

由此,我的意思是64位整数类型可以表示其范围内的每个值,但64位双精度不能。

例如,尝试将0.1存储到double中实际上不会给你0.1,它会给你类似的东西:

0.100000001490116119384765625

(实际上是最近的单个精度值,但同样的效果将适用于双精度)。

但是,如果问题是“你怎么用更少的比特来获得更大的范围?”,那么只是使用其中一些比特来扩展这个值。

经典示例,假设您有四位十进制数来存储值。使用整数,您可以表示00009999的数字。该范围内的精度是完美的,您可以表示每个积分值。

但是,让我们使用浮动点并使用最后一位数作为比例,以便数字1234实际上代表数字123 x 104

所以现在您的范围是0(由00000009代表)到999,000,000,000(由9999代表是999 x 109)。

但是你不能代表范围内的每个数字。例如,无法表示123,456,您可以使用的数字1233为您提供123,000。事实上,在整数值精度为四位数的情况下,现在只有三位。

基本上是IEEE754如何工作,牺牲了范围的精度。

答案 2 :(得分:6)

声明

这是尝试提供有关浮点编码如何工作的易于理解的解释。这是一种简化,它不包括真正的IEEE 754浮点标准的任何技术方面(归一化,有符号零,无穷大,NaN,舍入等)。但是,这里提出的想法是正确的。

了解浮点数如何工作受到以下事实的严重阻碍:计算机使用基数2中的数字,而人类不能轻易处理它们。我将尝试使用基础10解释浮点数的工作原理。

让我们使用符号和基数10数字(即我们每天​​使用的09的常用数字)构建浮点数表示。

我们假设我们有10个方格单元格,每个单元格可以包含一个符号(+-)或一个十进制数字(012345678或{{ 1}})。

我们可以使用10位数来存储有符号整数。符号的一位数和值的9位数:

9

这是值sign -+ +-------- 9 decimal digits -----+ v v v +---+---+---+---+---+---+---+---+---+---+ | + | 0 | 0 | 0 | 0 | 0 | 1 | 5 | 0 | 0 | +---+---+---+---+---+---+---+---+---+---+ 表示为整数的方式。

我们也可以使用它们来存储浮点数。例如,尾数为7位,指数为3位数:

1500

这是 +------ sign digits --------+ v v +---+---+---+---+---+---+---+---+---+---+ | + | 0 | 0 | 0 | 1 | 5 | 0 | + | 0 | 1 | +---+---+---+---+---+---+---+---+---+---+ |<-------- Mantissa ------->|<-- Exp -->| 作为浮点值的可能表示之一(使用我们的十进制数字表示)。

尾数(1500)的值为M,指数(+150)的值为E。上面表示的值是:

+1

范围

整数表示可以存储V = M * 10^E = 150 * 10^1 = 1500 -(10^9-1))和-999,999,999+(10^9-1))之间的有符号值。此外,它可以表示这些限制之间的每个整数值。更重要的是,每个值都有一个表示,而且确切。

浮点表示可以存储+999,999,999M之间的尾数(-999,999)以及+999,999和{之间的指数(E)的有符号值{1}}。

它可以存储-99+99之间的值。这些数字有-999,999*10^99个数字,远远超过上面用整数表示的最大数字的+999,999*10^99个数字。

精度不足

让我们注意,对于整数值,105存储符号和值的前6位(或更少),而9是不适合的数字位数M

E

让我们尝试使用我们的浮点编码来表示M

由于V = M * 10^E 仅限于V = +987,654,321,因此只能存储M+999,999+987,654E的最后3位数字不适合+3)。

将它们放在一起:

V

这不是M的原始值,但我们可以使用此表示法获得最佳近似值。

请注意,使用相同的值(+987,654 * 10^(+3) = +987,654,000 )近似(包括)V+987,654,000之间的所有数字。此外,无法存储大于+987,654,999的数字的十进制数字。

作为一般规则,对于大于M=+987,654, E=+3+999,999)的最大值的数字,此方法会为M+999.999之间的所有值生成相同的表示形式(整数或实数值,它并不重要)。

结论

对于较大的值(大于+999,999*10^E的最大值),浮点表示在它可以表示的数字之间存在间隙。随着+999,999*10^(E+1)-1的值增加,这些差距变得越来越大。

&#34;浮点&#34;的全部概念是存储十几个最具代表性的数字(数字的开头)和数字的大小。

让我们以光速为例。它的值约为M。如此庞大,对于大多数实际目的而言,如果它是E300,000 km/s,你就不在乎。

  

事实上,它甚至不是那么大,更好的近似是300,000.001 km/s

浮点数提取光速的重要特征:其幅度为几十万km / s(300,000.326 km/s),其值为299,792.458 km/s(几十万km /或多个)。

E=5

我们的浮点表示可以通过以下方式逼近:3speed of light = 3*10^5 km/s 299,792 km/s)。

答案 3 :(得分:3)

  

发生了什么样的魔法?

允许您表示101位数字

的同样魔力
10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 

作为

1.0 * 10100

这只是基础10而不是基数10:

0.57149369564113749110789177415267 * 2333

此表示法允许您以紧凑的方式表示非常大(或非常小)的值。您可以存储有效数字(例如尾数或分数)和指数,而不是存储每个数字。这样,数百个十进制数字长的数字可以用只占64位的格式表示。

指数允许浮点数表示如此大的范围值。指数值1024仅需要10位来存储,但21024是308位数。

权衡是,并非每个值都可以完全表示。对于64位整数,0264-1(或-263263-1)之间的每个值都具有精确的表示。由于几个原因,浮点数不是这样。首先,你只有这么多位,只给你很多精度数字。例如,如果您只有3位有效数字,那么您不能表示0.123和0.124之间,或1.23和1.24,或123和124,或1230000和1240000之间的值。当您接近范围的边缘时,可表示值之间的差距变得更大。

其次,就像有些值无法用有限数字表示(3/10给出非终止序列0.33333...10),有些值无法用有限数字表示位(1/10给出非终止序列1.100110011001...2)。

答案 4 :(得分:2)

也许你觉得&#34;存储一个N位的数字&#34;是一种基本的东西,而有各种方法可以做到这一点。事实上,更准确地说我们表示一个N位的数字,因为意义取决于我们采用的约定。原则上,我们可以采用任何我们喜欢的约定,其中N位模式代表不同的数字。存在二进制约定,用于unsigned long long和其他整数类型,以及用于double的尾数+指数约定,但我们也可以定义我们自己的(荒谬)约定,其中例如,所有位零意味着您需要指定的任何大数字。在实践中,我们通常使用允许我们使用运行程序的硬件有效地组合(添加,乘法等)数字的约定。

那就是说,你的问题必须通过比较最大二进制N位数和最大数量的2^exponent * mantissa来解答,其中exponent mantissa是E-和M位二进制数(在尾数的开头隐含1)。这是2^(2^E-1) * (2^M - 1),通常确实远大于2^N - 1

答案 5 :(得分:0)

Damon和Paxdiablo解释的一个小例子:

#include <stdio.h>

int main(void) {
    double d = 2LL<<52;
    long long ll = 2LL<<52;
    printf("d:%.0f  ll:%lld\n", d, ll);
    d++; ll++;
    printf("d:%.0f  ll:%lld\n", d, ll);
}

输出:

d:72057594037927936  ll:72057594037927936
d:72057594037927936  ll:72057594037927937

两个变量都会以相同的方式递增,移位为51或更小。