简体   繁体   English

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

[英]How to work with double bit codes and doubleToLongBits() in Java?

I was able to get the binary code of double numbers: 我能够得到双数的二进制代码:

Double d = 1.5E12;
long l = Double.doubleToLongBits(d);
String bin = Long.toBinaryString(l);
System.out.println(bin);
System.out.println(bin.length());

However, the resulting code doesn't behave like a double bitcode, it doesn't make any sense: 但是,生成的代码的行为不像双位代码,也没有任何意义:

1.5E12   --> 100001001110101110100111110111101111001100000000000000000000000 (length: 63)
-1.5E12  --> 1100001001110101110100111110111101111001100000000000000000000000 (length: 64)
1.5E-12  --> 11110101111010011000110110011001000001110001001101111100011010 (length: 62)
-1.5E-12 --> 1011110101111010011000110110011001000001110001001101111100011010 (length: 64)

I tried to understand those number by grouping them: 我试图通过将它们分组来了解这些数字:

1.5E12   -->     10000100111  0101110100111110111101111001100000000000000000000000
-1.5E12  --> 1   10000100111  0101110100111110111101111001100000000000000000000000
1.5E-12  -->   (0)1111010111  1010011000110110011001000001110001001101111100011010
-1.5E-12 --> 1   01111010111  1010011000110110011001000001110001001101111100011010

First of all it adds/removes a bit for the minus signs (that's a bad thing...speaking of parsers). 首先,它为负号添加/删除了一些(这是一件坏事……说到解析器)。 But even more the exponent shows very dubious numbers: Eg 10000100111 is supposed 1035 (= 1023 + 12) instead of 1063 (1063 - 1023 = 40!), and (0)1111010111 is supposed to be 1011 (= 1023 - 12) instead of 983 (983 - 1023 = -40!). 但是更重要的是,该指数显示了非常可疑的数字:例如10000100111应该是1035(= 1023 + 12)而不是1063(1063-1023 = 40!),而(0)1111010111应该是1011(= 1023-12) 983(983-1023 = -40!)。

Does anyone know how to read this double bit code? 有谁知道如何读取此两位代码? Ie how to get the right exponent and the mantisse out of above bit codes? 即如何从上述位代码中获取正确的指数和螳螂?

(An example: How to get the value 1.5E12 back again out of the bit code? 100001001110101110100111110111101111001100000000000000000000000 --?--> 1.5E12) (示例:如何从位代码中再次获取值1.5E12?100001001110101110100111110111101111001100000000000000000000000-?-> 1.5E12)

UPDATE: 更新:

Using the mask from the Java API I ended up to get the values like this: 使用Java API中的掩码,我最终得到了这样的值:

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;

And I could easily print it: 而且我可以轻松地打印它:

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));

With the Double 1.5E12 I got this result: 使用Double 1.5E12,我得到了以下结果:

sign-bin: 0
expn-bin: 10000100111
sgnf-bin: 101110100111110111101111001100000000000000000000000
sign-string: 0
expn-string: 1063
sgnf-string: 1640400372629504

Do you know how to get the "real" decimal values out of them? 您知道如何从中获取“实际”十进制值吗? (Eg 1640400372629504 --> 1.5 and 1063 --> E12) (例如1640400372629504-> 1.5和1063-> E12)

That the 0 for the sign is "missing" is just a number writing convention. 符号的0为“缺失”只是数字书写约定。

In the decimal system you will write 1067 instead of 0001067, right? 在十进制系统中,您将输入1067而不是0001067,对吗? This is just wha Java did. 这就是Java所做的。 Th 0s exist in memory, they are just not displayed, because you don't need them forth number. 内存中存在0,只是不显示它们,因为您不需要第四个数字。


Also this is a floating point representation of a binary number. 这也是二进制数的浮点表示。

If you convert 1.5*10^-12 to a binary number, it will have a lot of 0s at the beginning (I started, but then my paper ended - there are definitely more than 12 0s at the beginning). 如果将1.5 * 10 ^ -12转换为二进制数,则开头将有很多0(我开始了,但随后我的论文结束了-开头肯定有12个以上的0)。 This binary number is then normalized (so that there is only one 1 before the point) and the exponent of this normalization is used as the exponent. 然后将该二进制数归一化(以便在该点之前只有一个1),并将此归一化的指数用作指数。 I guess the binary exponent of 1.5*10^-12 is indeed 40. 我猜1.5 * 10 ^ -12的二进制指数确实是40。

In other words: the exponent of an IEEE number represents the value 2^exponent, not 10^exponent, like we use it in the decimal system. 换句话说:IEEE数字的指数表示值2 ^指数,而不是10 ^指数,就像我们在十进制中使用它一样。


According to this page you will need to multiply each digit of the mantisse with the corresponding power of 2 starting with 2^-1, then 2^-2 and so on and add them up. 根据此页面,您需要将螳螂的每个数字乘以2的相应幂,从2 ^ -1开始,然后是2 ^ -2,依此类推,然后将它们相加。

Then you use this formula: 然后使用以下公式:

(-1)^(sign bit) * (1+fraction) * 2^(exponent - bias) (-1)^(符号位)*(1+分数)* 2 ^(指数-偏差)

where fraction is the number you calculated from the mantisse. 其中fraction是您从螳螂计算出的数字。 exponent is the decimal representation of the exponent. exponentexponent的十进制表示形式。 bias depends on your precision, in your case double precision, which means 1023. For single precision it's only 127. bias取决于您的精度,在您的情况下为双精度,即1023。对于单精度,只有127。


You cannot directly convert the binary representation parts (say the binary exponent) to the decimal counterparts (the decimal exponent). 您不能将二进制表示形式的部分(例如二进制指数)直接转换为十进制对应部分(十进制指数)。 At least I don't konw how. 至少我不知道怎么做。

So you will need to convert the complete number to decimal and then split it into the number and its exponent. 因此,您需要将完整的数字转换为十进制,然后将其拆分为数字及其指数。

Here is the Java code to convert the binary representation to decimal: 这是将二进制表示形式转换为十进制的Java代码:

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));
}

Note that the result is not exactly accurete, since the floating point arithmetics in computers are not accurate and there is a limited sapce in which to store the information. 请注意,由于计算机中的浮点算术不准确,并且存储信息的空间有限,因此结果并不完全准确。

I dont't know how to extract the number and it's exponent from a double. 我不知道如何提取数字,它是从双精度指数中提取的。 My best guess is manipulation the String representation linke this: 我最好的猜测是操纵String表示链接此:

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]);

I hope this helped. 希望对您有所帮助。

As the Wikipedia page says,value of the number is 就像Wikipedia页面上所说,数字的值是

IEEE 488双精度数字值的表达式 .

So the binary exponent is 1063 - 1023 = 40. This makes sense because it multiplies the mantissa by 2^40, which is about 10^12 (because 2^10 is about 1000, 2^40 will be 1000^4). 因此二元指数为1063-1023 =40。这是有道理的,因为它将尾数乘以2 ^ 40,大约是10 ^ 12(因为2 ^ 10大约是1000,所以2 ^ 40将是1000 ^ 4)。 (This method of coding exponents is called zero-offset. It's used rather than 2s complement because it leads to simpler hardware.) (这种对指数进行编码的方法称为零偏移。使用它而不是2s补码,因为它可以简化硬件。)

The mantissa raw bits are 0101110100111110111101111001100000000000000000000000, so prepending the 1 as the formula above says, we have: 尾数的原始位是0101110100111110111101111001100000000000000000000000,因此在上面的公式说的1之前,我们有:

1.0101110100111110111101111001100000000000000000000000

Now, shifting the binary point 40 places to the right to account for the exponent, we have: 现在,将二进制点右移40位以说明指数,我们可以:

10101110100111110111101111001100000000000.000000000000

Happily, in decimal this is the integer 1500000000000, exactly what we want. 令人高兴的是,十进制是整数1500000000000,正是我们想要的。

From this you should be able to get the algorithm. 由此您应该能够获得算法。 The first step is to compute the integer part (just as I have done above) and use the standard algorithm for printing integers: 第一步是计算整数部分(就像我在上面所做的那样),并使用标准算法打印整数:

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

This leaves the fraction bits. 剩下分数位。 If there are N bits, then treat them as an N-bit integer and divide by 2^N. 如果有N位,则将它们视为N位整数并除以2 ^ N。 For example, a 1-bit fraction of 1 is 1/2. 例如,1的1位小数是1/2。 You can get decimal digits by implementing long division. 您可以通过执行长除法来获取十进制数字。 For example binary .11 is 3/4. 例如,二进制.11是3/4。 Implementing long division, we first get 3*10/4 = 7R2. 实施长除法,我们首先得到3 * 10/4 = 7R2。 Then 2*10/4 = 7R0. 然后2 * 10/4 = 7R0。 So the decimal digits are .75. 因此,十进制数字为.75。 In pseudocode: 用伪代码:

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)

Note these algorithms are conceptual, not practical. 注意,这些算法是概念性的,而不是实用的。 For one thing, they assume arbitrary precision integers. 一方面,它们假定任意精度的整数。 In practice you don't want this overhead, so calculations are actually done in floating point. 实际上,您不需要这种开销,因此计算实际上是在浮点中完成的。 The good old book Algorithms + Data Structures = Programs by Wirth goes into some detail about how this works. 沃思(Wirth)的老书《 算法+数据结构=程序》深入探讨了它的工作原理。

If you are implementing printf or similar library function, the details of base conversion are extremely hairy to get right with good speed. 如果要实现printf或类似的库函数,则基本转换的细节非常繁琐,无法快速获得正确的结果。 By "right" I mean being able to print a number in base-10 and read that representation back in with perfect confidence that you'll get exactly the same number. “正确”是指能够以10为底打印一个数字,并完全放心地读回该表示形式,以确保您获得完全相同的数字。 Don't underestimate this problem. 不要小看这个问题。

Also, as others have said your Java print routines are just eliding all leading zeros because they're intended for people, and people usually don't care about leading zeros. 而且,正如其他人所说的那样,您的Java打印例程只是消除了所有的前导零,因为它们是为人而设计的,人们通常并不关心前导零。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM