Вопрос

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: E.g. 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!).

Does anyone know how to read this double bit code? I.e. 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)

UPDATE:

Using the mask from the Java API I ended up to get the values like this:

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:

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? (E.g. 1640400372629504 --> 1.5 and 1063 --> E12)

Это было полезно?

Решение

That the 0 for the sign is "missing" is just a number writing convention.

In the decimal system you will write 1067 instead of 0001067, right? This is just wha Java did. Th 0s exist in memory, they are just not displayed, because you don't need them forth number.


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). 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. I guess the binary exponent of 1.5*10^-12 is indeed 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.


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.

Then you use this formula:

(-1)^(sign bit) * (1+fraction) * 2^(exponent - bias)

where fraction is the number you calculated from the mantisse. exponent is the decimal representation of the exponent. bias depends on your precision, in your case double precision, which means 1023. For single precision it's only 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:

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[] 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

expression for IEEE 488 double precision number value .

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). (This method of coding exponents is called zero-offset. It's used rather than 2s complement because it leads to simpler hardware.)

The mantissa raw bits are 0101110100111110111101111001100000000000000000000000, so prepending the 1 as the formula above says, we have:

1.0101110100111110111101111001100000000000000000000000

Now, shifting the binary point 40 places to the right to account for the exponent, we have:

10101110100111110111101111001100000000000.000000000000

Happily, in decimal this is the integer 1500000000000, exactly what we want.

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. For example, a 1-bit fraction of 1 is 1/2. You can get decimal digits by implementing long division. For example binary .11 is 3/4. Implementing long division, we first get 3*10/4 = 7R2. Then 2*10/4 = 7R0. So the decimal digits are .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.

If you are implementing printf or similar library function, the details of base conversion are extremely hairy to get right with good speed. 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. 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.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top