使用 2 "float" 秒模拟"double"

Emulate "double" using 2 "float"s

本文关键字:模拟 double float 使用      更新时间:2023-10-16

我正在为只支持32位单精度浮点运算的嵌入式硬件编写程序。然而,我正在实现的算法需要64位双精度加法和比较。我试图使用两个float的元组来模拟double数据类型。因此,double d将被模拟为包含以下元组的struct: (float d.hi, float d.low)

使用字典顺序进行比较应该很简单。然而,添加有点棘手,因为我不确定我应该使用哪个基础。应该是FLT_MAX吗?我怎样才能发现进位?

如何做到这一点?


Edit (clear):我需要额外的有效数字而不是额外的范围。

double-float是一种技术,它使用一对单精度数字来实现几乎是单精度算术精度的两倍,同时稍微缩小单精度指数范围(由于范围远端的中间下溢和溢出)。基本算法是由T.J. Dekker和William Kahan在20世纪70年代开发的。下面我列出了两篇最近的论文,它们展示了这些技术如何适用于gpu,然而,这些论文中涵盖的大部分材料都是独立于平台的,因此应该对手头的任务有用。

https://hal.archives - ouvertes.fr/hal 00021443纪尧姆·达·格拉帕拉,大卫·德福图形硬件上浮点-浮点运算符的实现第7届实数与计算机会议,RNC7.

http://andrewthall.org/papers/df64_qf128.pdf安德鲁ThallGPU计算的扩展精度浮点数。

这可不简单。

一个浮点数(IEEE 754单精度)有1个符号位,8个指数位和23个尾数位(实际上是24个)。

双精度(IEEE 754双精度)有1个符号位,11个指数位和52个尾数位(有效值为53)。

你可以从你的一个浮点数中使用符号位和8个指数位,但是你如何从另一个浮点数中获得3个指数位和29个尾数?

也许其他人可以想出一些聪明的办法,但我的答案是"这是不可能的"。(或者至少,"不比使用64位结构体并实现自己的操作更容易")

这在一定程度上取决于您想要执行的操作类型。如果你只关心加减法,卡汉求和是一个很好的解决方案。

如果您既需要精度又需要大范围,则需要双精度浮点数的软件实现,例如SoftFloat。

(此外,基本原理是将每个值的表示(例如64位)分解为它的三个组成部分-符号,指数和尾数;然后根据指数的差异移动一部分的尾数,根据符号位对另一部分的尾数进行加减,并可能通过移动尾数并相应地调整指数来重新规范化结果。在此过程中,有许多繁琐的细节需要考虑,以避免不必要的精度损失,并处理特殊值,如无穷大,nan和非规范化数字。

给定超过23个数量级的高精度的所有约束,我认为最有效的方法是实现自定义算术包。

一个快速调查显示Briggs的doubledouble c++库应该满足你的需求。看到这个。[*]默认实现是基于double来实现30位有效数字的计算,但很容易重写为使用float来实现13或14位有效数字。这可能足以满足您的需求,如果小心地隔离具有相似大小值的加法操作,只在最后的操作中将极值加在一起。

要注意,注释提到了混淆x87控制寄存器。我没有检查细节,但这可能会使代码太不可移植,不适合您使用。


[*]这篇文章链接了c++源代码,但只有压缩后的tar文件不是死链接。

这类似于许多编译器在一些只支持硬件double计算的机器上对long double使用的双双算法。它也被用作没有double支持的旧NVIDIA gpu上的float-float。参见在GPU上用2个FP32仿真FP64。这样计算将比软件浮点库快得多。

然而,在大多数微控制器中,没有硬件支持float s,因此它们纯粹在软件中实现。正因为如此,使用float-float可能会而不是提高性能,并引入一些内存开销来节省额外的指数字节。

如果您确实需要更长的尾号,请尝试使用自定义浮点库。您可以选择任何对您来说足够的东西,例如,如果只需要40位尾数和7位指数,则更改库以适应您自己的新的48位浮点类型。不需要花时间计算/存储不必要的16位了。但是这个库应该非常高效,因为编译器的库通常对自己的float类型进行汇编级优化。

另一个可能有用的基于软件的解决方案:GNU MPFR
它处理了许多其他特殊情况,并允许任意精度(优于64位双精度),否则您必须自己处理。

这是不实际的。如果是这样,每个嵌入式32位处理器(或编译器)都会通过这样做来模拟双精度。据我所知,目前还没有人这样做。大多数只是用float代替double

如果你需要的是精度而不是动态范围,你最好的选择是使用定点。如果编译器支持64位,这也会更容易。