【问题标题】:Convert 80-bit extended precision in java [closed]在java中转换80位扩展精度[关闭]
【发布时间】:2016-06-10 17:09:17
【问题描述】:

我正在开发一个程序,它将真正旧的 Open Access 4 .df 文件转换为 其他格式并创建数据库脚本 - 我已经能够转换每个 除十进制类型外的可能类型。我发现字节顺序必须是 80 位扩展精度。我已经尝试自己进行转换,但我无法在 Java 中进行从 80 位扩展精度 (https://en.wikipedia.org/wiki/Extended_precision#x86_Extended_Precision_Format) 到 String 和 String 到 80 位扩展精度的转换。

  1. 示例:

价值:1,235

df 文件中的十六进制:40 00 9e 06 52 14 1e f0 db f6

  1. 示例:

价值:0,750

df 文件中的十六进制:3f ff c0 00 00 00 00 00 00 00

也许有人可以帮助转换?

最好的问候

@EJP 评论: 好的,我正在丢弃最后 16 位,所以我得到了 3f ff c0 00 00 00 00 00 关于 (Java - Convert hex to IEEE-754 64-bit float - double precision) 我尝试将其转换为

String hex = "3fffc00000000000";
long longBits = Long.valueOf(hex ,16).longValue();
double doubleValue = Double.longBitsToDouble(longBits);

但结果是 1,984375 而不是 0,750

【问题讨论】:

  • 我不知道“二进制字符串”是什么意思,但你所要做的就是丢弃最后 16 位并将其余的转换为double。不难,在 JDK API 中都可以实现。
  • @ Jean-François Savard - Long.valueOf(hex ,16) 将十六进制作为输入 ;) 所以 Long.valueOf(hex ,16) 和 Long#decode 的结果相同。
  • @EJP,你不能只扔掉最后 16 位,因为格式不同。指数是一个 15 位,另一个是 11 位。
  • 只扔掉 16 位看起来不是个好主意 - 看看这个话题 stackoverflow.com/questions/2963055/… 还有更多工作要做
  • 知道图书馆吗?这个问题变得离题了,我建议你edit 成形。如果您不知道怎么做,可以向回答者寻求帮助

标签: java type-conversion extended-precision


【解决方案1】:

我不确定它是否有资格作为答案,或者是否可以回答,因为...您确定它是标准的 80 位浮点格式吗?因为它看起来有点奇怪。以你的第一个例子:

40 00 9e...

这里,0x4000−16383=1 是指数。尾数以MSB1开头,对应整数部分,所以它的形式应该是1,... x 2^1,大于2。但是你说应该是1235。没有意义。

我在 Java 中创建了这个丑陋的 hack,它可以组装双精度:

long high = 0x40_00L;
long low = 0x9e_06_52_14_1e_f0_db_f6L;
long e = (((high & 0x7FFFL) - 16383) + 1023) & 0x7FFL;
long ld = ((high & 0x8000L) << 48)
          | (e << 52)
          | ((low >>> 11) & 0xF_FFFF_FFFF_FFFFL);
System.out.printf("%16X\n", ld);
double d = Double.longBitsToDouble(ld);
System.out.println(d);

假设输入是正确的 80 位值,这个片段应该可以正常工作,除了它没有正确舍入、不检查溢出、不处理非规范化值并且可能无法处理像 NaN 这样的特殊值。对于此输入,它会打印

4003C0CA4283DE1B
2.46913578

不是 1,235!然后我使用了这段 C++ 代码(用 GCC 编译,其中long double 是 80 位格式):

#include <stdio.h>

int main() {
    long long high = 0x4000;
    long long low = 0x9e0652141ef0dbf6;
    long double d;
    char *pd = (char*)&d, *ph = (char*)&high, *pl = (char*)&low;
    for (int i = 0; i < 8; ++i) {
        pd[i] = pl[i];
    }
    for (int i = 0; i < 2; ++i) {
        pd[8 + i] = ph[i];
    }
    printf("%lf\n", (double) d);
}

它还打印 2.469136。

【讨论】:

  • OP 可能来自使用逗号作为小数分隔符的语言环境...
  • 您好,感谢您的回答 - 用一些值尝试了你的丑陋黑客,是的......你的结果除以 2 是确切的值......
  • @Alnitak,我也来自使用逗号的语言环境,尽管我个人更喜欢点。但它与示例有什么关系?它是 2 的数量级,而不是数千的数量级。无论语言环境是什么,一个点乘以 2 不可能是 1,235。它被 2 关闭的事实意味着格式错误或转换错误。而且由于C++打印的是2.46...,就说明格式不对。
  • @Sergey Tachenov 我有一个用 ida 反编译的转换器 - 我会检查值 - 也许你是对的 - 但开发 Open Access 4 的公司可能会混淆 .df 的值文件。没有信息描述该 df 文件等中的元信息。
  • @daTake,请注意我搞砸了符号位(使用错误的掩码来提取它)。现在修复了。
猜你喜欢
  • 2011-02-27
  • 2012-11-08
  • 2018-12-01
  • 2012-11-23
  • 2013-05-23
  • 1970-01-01
  • 1970-01-01
  • 2010-10-11
  • 1970-01-01
相关资源
最近更新 更多