首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在Java中使用双位代码和doubleToLongBits()?

如何在Java中使用双位代码和doubleToLongBits()?
EN

Stack Overflow用户
提问于 2014-05-13 12:34:13
回答 2查看 1.1K关注 0票数 1

我得到了双数的二进制代码:

代码语言:javascript
复制
Double d = 1.5E12;
long l = Double.doubleToLongBits(d);
String bin = Long.toBinaryString(l);
System.out.println(bin);
System.out.println(bin.length());

但是,生成的代码不像双比特代码,它没有任何意义:

代码语言:javascript
复制
1.5E12   --> 100001001110101110100111110111101111001100000000000000000000000 (length: 63)
-1.5E12  --> 1100001001110101110100111110111101111001100000000000000000000000 (length: 64)
1.5E-12  --> 11110101111010011000110110011001000001110001001101111100011010 (length: 62)
-1.5E-12 --> 1011110101111010011000110110011001000001110001001101111100011010 (length: 64)

我试图通过分组来理解这些数字:

代码语言:javascript
复制
1.5E12   -->     10000100111  0101110100111110111101111001100000000000000000000000
-1.5E12  --> 1   10000100111  0101110100111110111101111001100000000000000000000000
1.5E-12  -->   (0)1111010111  1010011000110110011001000001110001001101111100011010
-1.5E-12 --> 1   01111010111  1010011000110110011001000001110001001101111100011010

首先,它为减号添加/删除了一点(这是解析器的糟糕thing...speaking )。但更甚的是,指数显示了非常可疑的数字:例如10000100111被认为是1035 (= 1023 + 12),而不是1063 (1063 - 1023 = 40!),(0)1111010111应该是1011 (= 1023-12)而不是983 (983-1023= -40!)。

有人知道怎么读这个双位码吗?即,如何从以上位码中求出正确的指数和伪码?

(例如:如何将1.5E12的值从位代码中再次提取出来? 100001001110101110100111110111101111001100000000000000000000000 -> 1.5E12)

更新:

通过使用Java的掩码,我最终得到了如下的值:

代码语言:javascript
复制
static final long SIGN = 0x8000000000000000L;
static final long EXPN = 0x7ff0000000000000L;
static final long SGNF = 0x000fffffffffffffL;

Double d = ...;
long lng = Double.doubleToLongBits(d);
String bin = Long.toBinaryString(lng);
long sign = (lng & SIGN) >>> (bin.length()-1);
long expn = (lng & EXPN) >>> 52;
long sgnf = lng & SGNF;

我可以轻松地打印出来:

代码语言:javascript
复制
System.out.println("sign-bin: "+Long.toBinaryString(sign));
System.out.println("expn-bin: "+Long.toBinaryString(expn));
System.out.println("sgnf-bin: "+Long.toBinaryString(sgnf));
System.out.println("sign-string: "+Long.toString(sign));
System.out.println("expn-string: "+Long.toString(expn));
System.out.println("sgnf-string: "+Long.toString(sgnf));

在双1.5E12中,我得到了如下结果:

代码语言:javascript
复制
sign-bin: 0
expn-bin: 10000100111
sgnf-bin: 101110100111110111101111001100000000000000000000000
sign-string: 0
expn-string: 1063
sgnf-string: 1640400372629504

你知道如何从它们中提取“实”十进制值吗?(例如1640400372629504 -> 1.5及1063年-> E12)

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-05-13 13:14:10

标志的0是“缺失”,这只是一个数字书写惯例。

在十进制中,你将写1067,而不是0001067,对吗?这正是Java所做的。它存在于内存中,它们只是没有显示,因为你不需要它们的第四位。

这也是二进制数的浮点表示形式。

如果你把1.5*10^-12转换成二进制数,它在开始的时候会有很多0(我开始了,但是我的论文结束了--开始的时候肯定有超过12 0)。然后对这个二进制数进行归一化(因此在点之前只有一个1),并使用这个归一化的指数作为指数。我猜1.5*10^-12的二元指数确实是40。

换句话说: IEEE数的指数表示值2^指数,而不是10^指数,就像我们在十进制中使用的那样。

根据此页,你需要用相应的2的幂乘每个数字,从2^-1开始,然后2^-2等等,然后把它们加起来。

然后使用这个公式:

(-1)^(符号位)* (1+fraction) *2^(指数偏差)

其中fraction是你从曼蒂塞计算出来的数字。exponent是指数的十进制表示。bias取决于您的精度,在您的例子中是双精度,这意味着1023。单精度只有127。

不能直接将二进制表示部分(例如二进制指数)转换为十进制对应部分(十进制指数)。至少我不知道怎么做。

所以你需要把完整的数字转换成十进制,然后把它分解成数字和指数。

下面是将二进制表示转换为十进制的Java代码:

代码语言:javascript
复制
static final long SIGN = 0x8000000000000000L;
static final long EXPN = 0x7ff0000000000000L;
static final long SGNF = 0x000fffffffffffffL;

public static void main(String[] args){
    Double d = -0.0000000000015;//1500000000000d;
    long lng = Double.doubleToLongBits(d);
    String bin = Long.toBinaryString(lng);
    long sign = (lng & SIGN) >>> (bin.length()-1);
    long expn = (lng & EXPN) >>> 52;
    long sgnf = lng & SGNF;



    System.out.println("sign-bin: "+Long.toBinaryString(sign));
    System.out.println("expn-bin: "+Long.toBinaryString(expn));
    System.out.println("sgnf-bin: "+Long.toBinaryString(sgnf));
    System.out.println("sign-string: "+Long.toString(sign));
    System.out.println("expn-string: "+Long.toString(expn));
    System.out.println("sgnf-string: "+Long.toString(sgnf));

    String mantisse = Long.toBinaryString(sgnf);
    int pow2 = 2;
    double fraction = 0;
    for(int i = 0; i < mantisse.length(); i++){
        if(mantisse.charAt(i) == '1'){
            double curr = 1.0/pow2;
            fraction += Double.isInfinite(curr)? 0: curr;
        }
        //System.out.println(fraction + " " + pow2);
        pow2 <<= 1;
    }

    System.out.println((1+fraction));
    System.out.println("Back to decimal: " + (sign == 1?(-1):1) * (1+fraction) * Math.pow(2, expn - 1023));
}

请注意,结果并不完全正确,因为计算机中的浮点算法不准确,而且存储信息的智库有限。

我不知道如何从双倍中提取数字和指数。我的最佳猜测是操作字符串表示形式linke如下:

代码语言:javascript
复制
String[] number = Double.toString(dBack).split("E");
System.out.println("Decimal exponent: " + (number.length == 2?number[1]: 1));
System.out.println("Decimal mantisse: " + number[0]);

希望这能帮上忙。

票数 4
EN

Stack Overflow用户

发布于 2014-05-13 23:51:39

如维基百科页面所述,该数字的值为

所以二元指数是1063 - 1023 = 40。这是有意义的,因为它将尾数乘以2^40,约为10^12 (因为2^10约为1000,2^40将为1000^4)。(这种指数编码方法称为零偏移距。它被使用,而不是2s的补充,因为它导致了更简单的硬件。)

尾数的原始位是0101110100111110111101111001100000000000000000000000,,所以前面的公式是1,我们有:

代码语言:javascript
复制
1.0101110100111110111101111001100000000000000000000000

现在,将二进制点40位移到指数的右边,我们有:

代码语言:javascript
复制
10101110100111110111101111001100000000000.000000000000

幸运的是,在十进制中,这是整数1500000000000,这正是我们想要的。

这样你就能得到算法了。第一步是计算整数部分(正如我前面所做的),并使用打印整数的标准算法:

代码语言:javascript
复制
i = 0;
while (int_part > 0) {
   digit[i] = int_part % 10
   int_part = int_part / 10
   i = i + 1
}
if (i == 0) return '0'
else reverse digits[0..i-1] and return

这就留下了分数位。如果有N位,那么把它们当作一个N位整数,除以2^N。例如,1位分数1是1/2。你可以通过实现长除法得到十进制数。例如,二进制.11是3/4。实现长除法,我们首先得到3*10/4 = 7R2。然后2*10/4 = 7R0。所以十进制数字是.75。伪码:

代码语言:javascript
复制
num = frac_part
den = 2 ^ (number of bits in frac_part)
i = 0
do {
  num = num * 10
  digit[i] = num / den
  num = num % den
  i = i + 1
} while (num != 0)

注意,这些算法是概念性的,不实用的。首先,它们假定任意精度整数。实际上,您不需要这样的开销,所以计算实际上是用浮点进行的。Wirth写的一本好的老书“算法+数据结构=程序”详细介绍了它是如何工作的。

如果您正在实现printf或类似的库函数,则基本转换的细节非常复杂,很难以良好的速度得到正确的处理。我所说的“对”是指能够在基数10中打印一个数字,并以完全有信心的方式将这个表示读回来,相信你会得到完全相同的数字。不要低估这个问题。

另外,正如其他人所说的,您的Java打印例程只是在省略所有的前导零,因为它们是为人设计的,而且人们通常不关心前导零。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/23631417

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档