编码 java Double as asn1 Real {sign, mantissa, base, exponent}
encoding java Double as asn1 Real {sign, mantissa, base, exponent}
我已经实现了自己的 ASN.1 框架,我的最后一项是将 Java Double 编码为 ASN.1 Real。首先 link 是我的库,然后是 ASN.1 Reals 规范的 link。我的问题在我的 encodeDoubleValue 中访问正确的尾数。求助!
https://github.com/ZiroZimbarra/ASN1
我在 Reals 测试中的代码如下所示:
testRealBytes(new byte[] {9, 5, (byte) 0x80, (byte) 0xFE, (byte) 0x55, (byte) 0x55, (byte) 0x55}, 1398101.25);
这是我的测试实现,我的问题出现在第三行代码的编码中。
private void testRealBytes(byte[] bytes, Object testObj) throws IOException, InstantiationException, IllegalAccessException {
Object obj = (Double) ASN1InputStream.decode(bytes);
assertTrue(obj.equals(testObj));
byte[] newBytes = ASN1OutputStream.encodeObject(obj);
assertTrue(Arrays.equals(bytes, newBytes));
}
这是我的 encodeValue 方法:
public void encodeDoubleValue(Double doubleObj, ASN1OutputStream derStream) throws IOException {
if(doubleObj == 0) {return; }
if(doubleObj == Double.POSITIVE_INFINITY) { derStream.write(0x40); return; }
if(doubleObj == Double.NEGATIVE_INFINITY) { derStream.write(0x41); return; }
long bits = Double.doubleToLongBits(doubleObj);
long mantissa = (bits & 0x000fffffffffffffL);
int exponent = Integer.valueOf(Long.valueOf(((bits & 0x7FF0000000000000L) >> 52) - 1023).toString());
int base = 2;
byte signByte = (byte) (((bits & 0x8000000000000000L) > 1) ? 0x40 : 0x00);
ASN1OutputStream exponentStream = derStream.newStream();
new ASN1IntegerType().encodeValue(exponent, exponentStream);
byte[] exponentBytes = exponentStream.toByteArray();
switch(exponentBytes.length) {
case 1:
derStream.write(0x80 | (byte) signByte | 0x00);
break;
case 2:
derStream.write(0x80 | (byte) signByte | 0x01);
break;
case 3:
derStream.write(0x80 | (byte) signByte | 0x02);
break;
default:
derStream.write(0x80 | (byte) signByte | 0x03);
derStream.write(exponentBytes.length);
break;
}
derStream.write(exponentBytes);
byte[] leftBytes = Long.toUnsignedString(mantissa, 16).getBytes();
int length = leftBytes.length;
byte[] mantissaBytes = new byte[length];
for(int i = 0; i < length; i++) {
mantissaBytes[i] = leftBytes[length - i - 1];
}
for(byte b : mantissaBytes) {
derStream.write(b);
}
}
1398101.25 的结果是
[9, 15, -128, 20, 48, 48, 48, 48, 48, 48, 48, 52, 53, 53, 53, 53, 53]
而不是它应该是的
[9, 5, -128, -2, 85, 85, 85]
除了使用字符(BER/DER 仅用于 base=10)而不是位,正如 Kevin 评论的那样,并且还向后执行它们完全没有意义,您的错误是:
Java(和大多数流行的 CPU)使用的 IEEE-then-ISO/IEC 浮点方案,与大多数其他方案一样,在左侧有隐含的 binary/radix 点'mantissa'(更好,有效数)的,而不是 BER/DER 的右边,并且在该隐含点
[= 的左侧还有一个 'hidden' 1 位27=]
DER(但不是 BER)需要对尾数进行归一化(零除外,它已经是特殊情况)所以它的最低有效位是 1;对于 base=2 这只需要调整指数,但 8 或 16 也可能需要比例因子
此外,您不需要 ASN1OutputStream
来编码整数,因为 Java 的内置 BigInteger.toByteArray()
已经生成规范的大端二进制补码,而您不需要实际上不需要处理大于 2 个八位字节的指数长度,因为它们永远不会出现在 IEEE-ISO/IEC 值中。 (如果您想要 BigDecimal
值或类似值,可能需要它们。)
以下代码生成正确的预期值(不包括标签和长度):
double d = 1398101.25d;
ByteArrayOutputStream str = new ByteArrayOutputStream();
// for demo, use real output as needed
long bits = Double.doubleToRawLongBits(d);
int signByte = (int)(bits>>(63-6))&(1<<6) | (1<<7);
// shift to the correct place to start with, and pre-add bit 8
int exponent = ((int)(bits>>52)&0x7FF)-(1023+52);
// don't need to box/unbox to do arithmetic
long mantissa = (bits&0xFFFFFFFFFFFFFL) | (1L<<52);
// add the hidden bit
while( (mantissa&1)==0 ){ mantissa>>=1; exponent++; }
// normalize
byte[] exptbytes = BigInteger.valueOf(exponent).toByteArray();
if(exptbytes.length<3) str.write(signByte|(exptbytes.length-1));
else{ str.write(signByte|3); str.write(exptbytes.length); }
// only the if branch is actually needed
str.write (exptbytes);
str.write (BigInteger.valueOf(mantissa/*>>scale*/).toByteArray());
System.out.println(Arrays.toString(str.toByteArray()));
// for demo
我已经实现了自己的 ASN.1 框架,我的最后一项是将 Java Double 编码为 ASN.1 Real。首先 link 是我的库,然后是 ASN.1 Reals 规范的 link。我的问题在我的 encodeDoubleValue 中访问正确的尾数。求助!
https://github.com/ZiroZimbarra/ASN1
我在 Reals 测试中的代码如下所示:
testRealBytes(new byte[] {9, 5, (byte) 0x80, (byte) 0xFE, (byte) 0x55, (byte) 0x55, (byte) 0x55}, 1398101.25);
这是我的测试实现,我的问题出现在第三行代码的编码中。
private void testRealBytes(byte[] bytes, Object testObj) throws IOException, InstantiationException, IllegalAccessException {
Object obj = (Double) ASN1InputStream.decode(bytes);
assertTrue(obj.equals(testObj));
byte[] newBytes = ASN1OutputStream.encodeObject(obj);
assertTrue(Arrays.equals(bytes, newBytes));
}
这是我的 encodeValue 方法:
public void encodeDoubleValue(Double doubleObj, ASN1OutputStream derStream) throws IOException {
if(doubleObj == 0) {return; }
if(doubleObj == Double.POSITIVE_INFINITY) { derStream.write(0x40); return; }
if(doubleObj == Double.NEGATIVE_INFINITY) { derStream.write(0x41); return; }
long bits = Double.doubleToLongBits(doubleObj);
long mantissa = (bits & 0x000fffffffffffffL);
int exponent = Integer.valueOf(Long.valueOf(((bits & 0x7FF0000000000000L) >> 52) - 1023).toString());
int base = 2;
byte signByte = (byte) (((bits & 0x8000000000000000L) > 1) ? 0x40 : 0x00);
ASN1OutputStream exponentStream = derStream.newStream();
new ASN1IntegerType().encodeValue(exponent, exponentStream);
byte[] exponentBytes = exponentStream.toByteArray();
switch(exponentBytes.length) {
case 1:
derStream.write(0x80 | (byte) signByte | 0x00);
break;
case 2:
derStream.write(0x80 | (byte) signByte | 0x01);
break;
case 3:
derStream.write(0x80 | (byte) signByte | 0x02);
break;
default:
derStream.write(0x80 | (byte) signByte | 0x03);
derStream.write(exponentBytes.length);
break;
}
derStream.write(exponentBytes);
byte[] leftBytes = Long.toUnsignedString(mantissa, 16).getBytes();
int length = leftBytes.length;
byte[] mantissaBytes = new byte[length];
for(int i = 0; i < length; i++) {
mantissaBytes[i] = leftBytes[length - i - 1];
}
for(byte b : mantissaBytes) {
derStream.write(b);
}
}
1398101.25 的结果是
[9, 15, -128, 20, 48, 48, 48, 48, 48, 48, 48, 52, 53, 53, 53, 53, 53]
而不是它应该是的
[9, 5, -128, -2, 85, 85, 85]
除了使用字符(BER/DER 仅用于 base=10)而不是位,正如 Kevin 评论的那样,并且还向后执行它们完全没有意义,您的错误是:
Java(和大多数流行的 CPU)使用的 IEEE-then-ISO/IEC 浮点方案,与大多数其他方案一样,在左侧有隐含的 binary/radix 点'mantissa'(更好,有效数)的,而不是 BER/DER 的右边,并且在该隐含点
[= 的左侧还有一个 'hidden' 1 位27=]DER(但不是 BER)需要对尾数进行归一化(零除外,它已经是特殊情况)所以它的最低有效位是 1;对于 base=2 这只需要调整指数,但 8 或 16 也可能需要比例因子
此外,您不需要 ASN1OutputStream
来编码整数,因为 Java 的内置 BigInteger.toByteArray()
已经生成规范的大端二进制补码,而您不需要实际上不需要处理大于 2 个八位字节的指数长度,因为它们永远不会出现在 IEEE-ISO/IEC 值中。 (如果您想要 BigDecimal
值或类似值,可能需要它们。)
以下代码生成正确的预期值(不包括标签和长度):
double d = 1398101.25d;
ByteArrayOutputStream str = new ByteArrayOutputStream();
// for demo, use real output as needed
long bits = Double.doubleToRawLongBits(d);
int signByte = (int)(bits>>(63-6))&(1<<6) | (1<<7);
// shift to the correct place to start with, and pre-add bit 8
int exponent = ((int)(bits>>52)&0x7FF)-(1023+52);
// don't need to box/unbox to do arithmetic
long mantissa = (bits&0xFFFFFFFFFFFFFL) | (1L<<52);
// add the hidden bit
while( (mantissa&1)==0 ){ mantissa>>=1; exponent++; }
// normalize
byte[] exptbytes = BigInteger.valueOf(exponent).toByteArray();
if(exptbytes.length<3) str.write(signByte|(exptbytes.length-1));
else{ str.write(signByte|3); str.write(exptbytes.length); }
// only the if branch is actually needed
str.write (exptbytes);
str.write (BigInteger.valueOf(mantissa/*>>scale*/).toByteArray());
System.out.println(Arrays.toString(str.toByteArray()));
// for demo