[英]Java Double.doubleToLongBits into C# BitConverter.ToDouble
[英]How to work with double bit codes and doubleToLongBits() in Java?
我能够得到双数的二进制代码:
Double d = 1.5E12;
long l = Double.doubleToLongBits(d);
String bin = Long.toBinaryString(l);
System.out.println(bin);
System.out.println(bin.length());
但是,生成的代码的行为不像双位代码,也没有任何意义:
1.5E12 --> 100001001110101110100111110111101111001100000000000000000000000 (length: 63)
-1.5E12 --> 1100001001110101110100111110111101111001100000000000000000000000 (length: 64)
1.5E-12 --> 11110101111010011000110110011001000001110001001101111100011010 (length: 62)
-1.5E-12 --> 1011110101111010011000110110011001000001110001001101111100011010 (length: 64)
我试图通过将它们分组来了解这些数字:
1.5E12 --> 10000100111 0101110100111110111101111001100000000000000000000000
-1.5E12 --> 1 10000100111 0101110100111110111101111001100000000000000000000000
1.5E-12 --> (0)1111010111 1010011000110110011001000001110001001101111100011010
-1.5E-12 --> 1 01111010111 1010011000110110011001000001110001001101111100011010
首先,它为负号添加/删除了一些(这是一件坏事……说到解析器)。 但是更重要的是,该指数显示了非常可疑的数字:例如10000100111应该是1035(= 1023 + 12)而不是1063(1063-1023 = 40!),而(0)1111010111应该是1011(= 1023-12) 983(983-1023 = -40!)。
有谁知道如何读取此两位代码? 即如何从上述位代码中获取正确的指数和螳螂?
(示例:如何从位代码中再次获取值1.5E12?100001001110101110100111110111101111001100000000000000000000000-?-> 1.5E12)
更新:
使用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;
而且我可以轻松地打印它:
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));
使用Double 1.5E12,我得到了以下结果:
sign-bin: 0
expn-bin: 10000100111
sgnf-bin: 101110100111110111101111001100000000000000000000000
sign-string: 0
expn-string: 1063
sgnf-string: 1640400372629504
您知道如何从中获取“实际”十进制值吗? (例如1640400372629504-> 1.5和1063-> E12)
符号的0为“缺失”只是数字书写约定。
在十进制系统中,您将输入1067而不是0001067,对吗? 这就是Java所做的。 内存中存在0,只是不显示它们,因为您不需要第四个数字。
这也是二进制数的浮点表示。
如果将1.5 * 10 ^ -12转换为二进制数,则开头将有很多0(我开始了,但随后我的论文结束了-开头肯定有12个以上的0)。 然后将该二进制数归一化(以便在该点之前只有一个1),并将此归一化的指数用作指数。 我猜1.5 * 10 ^ -12的二进制指数确实是40。
换句话说:IEEE数字的指数表示值2 ^指数,而不是10 ^指数,就像我们在十进制中使用它一样。
根据此页面,您需要将螳螂的每个数字乘以2的相应幂,从2 ^ -1开始,然后是2 ^ -2,依此类推,然后将它们相加。
然后使用以下公式:
(-1)^(符号位)*(1+分数)* 2 ^(指数-偏差)
其中fraction
是您从螳螂计算出的数字。 exponent
是exponent
的十进制表示形式。 bias
取决于您的精度,在您的情况下为双精度,即1023。对于单精度,只有127。
您不能将二进制表示形式的部分(例如二进制指数)直接转换为十进制对应部分(十进制指数)。 至少我不知道怎么做。
因此,您需要将完整的数字转换为十进制,然后将其拆分为数字及其指数。
这是将二进制表示形式转换为十进制的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));
}
请注意,由于计算机中的浮点算术不准确,并且存储信息的空间有限,因此结果并不完全准确。
我不知道如何提取数字,它是从双精度指数中提取的。 我最好的猜测是操纵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]);
希望对您有所帮助。
就像Wikipedia页面上所说,数字的值是
。
因此二元指数为1063-1023 =40。这是有道理的,因为它将尾数乘以2 ^ 40,大约是10 ^ 12(因为2 ^ 10大约是1000,所以2 ^ 40将是1000 ^ 4)。 (这种对指数进行编码的方法称为零偏移。使用它而不是2s补码,因为它可以简化硬件。)
尾数的原始位是0101110100111110111101111001100000000000000000000000,因此在上面的公式说的1之前,我们有:
1.0101110100111110111101111001100000000000000000000000
现在,将二进制点右移40位以说明指数,我们可以:
10101110100111110111101111001100000000000.000000000000
令人高兴的是,十进制是整数1500000000000,正是我们想要的。
由此您应该能够获得算法。 第一步是计算整数部分(就像我在上面所做的那样),并使用标准算法打印整数:
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。 用伪代码:
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打印例程只是消除了所有的前导零,因为它们是为人而设计的,人们通常并不关心前导零。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.