【问题标题】:Confused with size of long and double [duplicate]对 long 和 double 的大小感到困惑 [重复]
【发布时间】:2013-09-08 17:41:49
【问题描述】:

看 Java(但在其他语言中可能相似或相同),long 和 double 都使用 8 个字节来存储一个值。

long 使用 8 个字节来存储从 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 的长整数

double 使用 8 个字节来存储从 -1.7E308 到 1.7E308 的双精度浮点数,最多 16 个有效数字。

我的问题是,如果两者都使用相同的字节数(8 字节或 2^64),如何双倍存储更长的数字? 1.7E308 比 9,223,372,036,854,775,807 大得多。

【问题讨论】:

  • 这样做会导致精度损失。阅读docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
  • 因为您可以将 1 加到任何 long 上,但您不能将 1.0 加到某些双精度上,例如 1.6E308。或者,如果您希望能够添加 0.00000000001,那么您的 double 数字不能超过 1.0,这比 long 小得多。
  • 浮点数存储在计算机中,相当于指数符号(有点像“1.234E56”,只是二进制)。另一方面,intlong 值存储在计算机中,相当于普通数字(例如,12345678,没有小数点也没有指数)。浮点数/双精度数的范围较大,但精度较小。
  • 据我研究,Java double 可以存储 15 位有效十进制数字。但是,长号码最多可以达到 19 位(922,...)。因此,问题应该是“15 位容量如何包含 19 位”。所以,stackoverflow.com/users/2662723/user2662723 的最后一条语句令人困惑。

标签: java memory-management


【解决方案1】:

64位可以存储的绝对信息量当然是一样的。

改变的是你赋予位的意义。

integerlong 变量中,使用的编码与您在日常生活中用于十进制数的编码相同,除了使用了第二个补码,但这并没有改变这一点很多,因为获得一个额外的数字只是一个技巧(同时只存储一个零而不是一个正数和一个负数)。

在浮点或双精度变量中,位分为两种:尾数和指数。这意味着每个双精度数的形状都类似于XXXXYYYYY,其数值类似于XXXX*2^YYYY。基本上你决定以不同的方式对它们进行编码,你得到的是你有相同数量的值,但它们以不同的方式分布在整个实数集上。

浮点数的最大值/最小值大于/小于整数的最大值/最小值这一事实并不意味着有效存储的数据量有任何影响。

【讨论】:

    【解决方案2】:

    本质上,double 可以存储更大的数字,因为它可以存储的数字之间的间隔更大。并非双精度范围内的每个整数都可以由该双精度表示。

    更具体地说,double 有一位 (S) 用于存储符号,11 位用于存储指数 E,以及 52 位精度,即尾数 (M)。

    对于大多数数字(有一些特殊情况),双精度数存储数字 (-1)^S * (1 + (M * 2^{-52})) * 2^{E - 1023},并且因此,当 E 很大时,将 M 更改 1 将使所得数字的大小变化比 1 大得多。正是这些巨大的差距使得双倍的范围比多头更大。

    【讨论】:

      【解决方案3】:

      Long 或 int 是有符号的实体,可以是正数或负数,但不能有任何小数部分。

      浮点或双精度类型在计算机中用于表示具有小数部分的数字。

      long 和 double 都是 64 位。 Long 有 1 位用于有符号(确定正数或负数),其余 63 位组成数字。所以范围可以是 -2^63 到 2^63-1

      双精度数以 IEEE 二进制浮点算术标准指定的不同方式表示,旨在在计算机中存储非常大的数字。 64位双精度表示为-[1位][11位指数][52位尾数]

      让我们看一个例子,将 100.25 转换为二进制形式存储为双精度

      1. 十进制100.25转换成二进制是1100100.01
      2. 然后将二进制 1100100.01 归一化为 1.10010001 * 2^6
      3. 6 是指数部分。我们选择 base 或 offset 为 1023,以便可以正确表示负数和正数。所以 6+1023=1029 是添加偏置后的偏移指数分量。 100000011是指数的二进制表示。

      4. 要从 1.10010001 计算尾数,我们忽略小数点右侧的 1,只使用小数点右侧的所有数字(即 10010001),并用零填充任何剩余的 52 位。 所以,现在尾数将是 1001 0001 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

      5. 所以,最后 64 位表示为 有符号位指数尾数 0 100000011 1001 0001 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

      【讨论】:

        【解决方案4】:

        阅读上面的 Delimanolis 语句,我测试了 long-to-double 转换中的精度损失 - 忽略了 500 大的整数值(见下文)。

        long L;
        double D;
        
        L = 922_3372_0368_5477_5807L;
        L -= 500;
        D = L;
        L = (long)D;
        
        System.out.println("D and L: " + D + " " + L);
        

        输出

        D and L: 9.223372036854776E18 9223372036854775807
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-08-15
          • 2015-02-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-11-28
          • 2016-02-21
          相关资源
          最近更新 更多