Java 中的 x86 80 位浮点类型

x86 80-bit floating point type in Java

提问人:box 提问时间:10/29/2015 最后编辑:Peter Cordesbox 更新时间:11/13/2023 访问量:1675

问:

我想模拟 x86 扩展精度类型,并在 Java 中对其他类型执行算术运算和强制转换。

我可以尝试使用 BigDecimal 实现它,但涵盖围绕 NaN、无穷大和强制转换的所有特殊情况可能是一项繁琐的任务。我知道有些库为其他浮点类型提供比 double 更高的精度,但我希望具有与 x86 80 位浮点数相同的精度。

有没有提供这种浮点类型的 Java 库?如果没有,您能否提供其他提示,以便以比提出自定义 BigDecimal 解决方案更少的努力来实现此类数据类型?

Java 浮点 精度 x87 x86 仿真

评论

0赞 Nayuki 10/30/2015
这听起来像是一个有趣的问题,但为什么要模拟 80 位扩展精度格式呢?为什么不模仿更精确的东西呢?
2赞 Nayuki 10/30/2015
在这种情况下,与标准 IEEE 754 相比,您将可以模拟对非正常数字的古怪处理:en.wikipedia.org/wiki/......
7赞 Louis Wasserman 10/30/2015
BigDecimal不适合这个问题; 表示 10 次幂的十进制数;你想要的类型表示幂为 2 的二进制数,将它们混合在一起并不是一个好的计划。BigDecimal
1赞 Durandal 10/30/2015
“不是一个好的计划”是轻描淡写的。“注定要失败的设计”更像是:)
1赞 supercat 10/30/2015
@NayukiMinase:80 位浮点没有与其他类型相同的次正常数,因为它使用显式而不是隐式前导 1。当存储指数低于最小允许值的结果时,需要进行一些特殊处理,但与其他格式相比,它比需要对加载和存储进行特殊处理的格式要麻烦得多。

答:

4赞 david 10/30/2015 #1

如果您知道您的 Java 代码实际上将在 x86 处理器上运行,请在汇编中实现 80 位算术(如果 C 编译器支持 C),并使用 JNI 进行调用。

如果您面向特定的非 x86 平台,请查看 qemu 代码。应该有某种方法可以撕掉执行 80 位浮点运算的部分。(编辑:qemu 的实现是 SoftFloat。用 JNI 调用它。

如果你真的想要跨平台的纯 Java 80 位算术,你可能仍然可以将它与开源 CPU 模拟器中的 C 实现进行比较,以确保你正在解决正确的极端情况。

评论

0赞 Nayuki 10/30/2015
我非常同意你的回答。深入研究 C/asm,或重用现有 C 代码,或构建与本机代码或现有仿真器进行比较的新实现。
0赞 box 10/31/2015
我想使用纯 Java 代码。我想我必须按照您的建议查看用另一种语言编写的现有实现。
-4赞 Joop Eggen 10/30/2015 #2

这与 java strictfp 选项相反,后者将计算限制为 8 个字节,而它执行 80 位。

所以我的答案是在 64 位机器上运行 JVM,也许在某些虚拟机管理程序/操作系统 VM 中,这样你就有了一个开发平台。

评论

3赞 box 10/30/2015
那行不通。strictfp 允许一些回旋余地,但依靠 JIT 实际使用 80 位浮点数作为中间值对我来说听起来是错误的。此外,strictfp(或反向选项)不会影响存储,存储仍将采用双倍位宽度。
1赞 Nayuki 10/30/2015
即使省略关键字,现代 JVM 也可能会使用 x86 SSE 指令来执行纯 64 位或 32 位 FP 数学运算,而不是使用 x87 的更宽寄存器。strictfp
4赞 supercat 10/30/2015 #3

80 位值最好是 a(尾数)和 an 的组合,表示指数和符号。对于许多操作,将 long 的上半部分和下半部分放入单独的“long”值可能是最实用的,因此将两个具有匹配符号和指数的数字相加的代码可能是这样的:longint

long resultLo = (num1.mant & 0xFFFFFFFFL)+(num2.mant & 0xFFFFFFFFL);
long resultHi = (num1.mant >>> 32)+(num2.mant >>> 32)+(resultLo >>> 32);
result.exp = num1.exp; // Should match num2.exp
if (resultHi > 0xFFFFFFFFL) {
  exponent++;
  resultHi = (resultHi + ((resultHi & 2)>>>1)) >>> 1; // Round the result
}
rest.mant = (resultHi << 32) + resultLo;

周围有点麻烦,但并非完全不可行。关键是 将数字分解成足够小的碎片,以便您可以进行所有数学运算 键入“long”。

顺便说一句,请注意,如果其中一个数字最初没有相同的指数, 有必要跟踪是否有任何位“从末端掉下来”,当 向左或向右移动以匹配第一个数字的指数,以便 之后能够正确地对结果进行四舍五入。

评论

0赞 box 11/6/2015
谢谢!这是帮助我实现的最有用的提示。