使用2个“浮动”模拟“双”

我正在编写一个只支持32位单精度浮点运算的embedded式硬件程序。 然而,我正在执行的algorithm需要64位双精度加法和比较。 我正在尝试使用两个float的元组来模拟double数据types。 所以一个double d将被模拟为一个包含元组的struct(float d.hi, float d.low)

使用字典顺序来进行比较应该很简单。 然而,加法是有点棘手,因为我不知道我应该使用哪个基地。 应该是FLT_MAX ? 我怎么能检测一个进位?

如何才能做到这一点?


编辑(清晰):我需要额外的有效数字,而不是额外的范围。

双浮点法是一种使用单精度数对来实现几乎两倍于单精度算术精度的技术,伴随着单精度指数范围的略微降低(由于范围远端的中间下溢和溢出) 。 基本algorithm由TJ Dekker和William Kahan在20世纪70年代开发。 下面我列出了两篇相当近期的论文,展示了如何将这些技术适用于GPU,但是这些论文所涉及的大部分内容都适用于独立平台,因此对于手头的任务应该是有用的。

float-float.pdf Guillaume DaGraça,David Defour在graphics硬件上实现float-float运算符,第七次实数会议和电脑,RNC7。

papers/df64_qf128.pdf Andrew Thall用于GPU计算的扩展精度浮点数。

这不会很简单。

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

双(IEEE 754双精度)具有1个符号位,11个指数位和52个尾数(实际上是53)。

你可以使用浮点数中的符号位和8位指数位,但是你怎样得到另外3个指数位和29位尾数呢?

也许别人可以拿出一些聪明的东西,但我的回答是“这是不可能的”。 (或者至less,“使用64位结构并实现自己的操作并不容易”)

这取决于你想要执行什么types的操作。 如果你只关心加法和减法, Kahan Summation可以是一个很好的解决scheme。

如果你同时需要精度和广泛的范围,你将需要一个双精度浮点软件实现,如SoftFloat 。

(另外,基本原理是将每个值的表示(例如64位)分解为三个部分 – 符号,指数和尾数;然后根据指数的差值移动一个部分的尾数,加到或从其他部分的尾数减去符号位,并且可能通过移位尾数和相应地调整指数来重新归一化结果。一路上有很多细节需要解释,以避免不必要的损失的准确性,并处理诸如无穷大,NaN和非规范化数字等特殊值。

这是不实际的。 如果是这样,每个embedded式32位处理器(或编译器)都会通过这样做来模拟双精度。 就目前而言,我没有意识到这一点。 他们中的大多数只是replace浮动双。

如果你需要精度而不是dynamic范围,你最好的办法就是使用固定点。 如果编译器支持64位,这也会更容易。

考虑到23级以上的高精度限制,我认为最有效的方法是实现自定义算术包。

一个快速调查显示布里格斯的 C ++库应该满足您的需求,然后一些。 看到这个 [*]默认实现是基于double实现30个有效数字的计算,但是很容易被重写为使用float来实现13或14个有效数字。 如果注意分离具有类似幅度值的加法运算,那么这对于您的要求可能就足够了,只是在最后的操作中将极端情况加在一起。

当心,评论提及了x87控制寄存器。 我没有检查细节,但这可能会使代码太不可移植,供您使用。


[*] C ++源文件被链接,但只有gzipped tar不是死链接。

另一种可能使用的基于软件的解决scheme: GNU MPFR
它处理许多其他特殊情况,并允许任意的精度(比64位双精度要好),否则你将不得不自己照顾自己。

这与许多编译器在某些仅支持硬件双重计算的计算机上计算long double的双重方法类似。 在这种情况下,计算速度非常快。

在大多数微控制器中,没有硬件支持浮点数,所以只能用软件来实现。 因此,使用float-float可能不会增加性能,并引入一些内存开销来节省指数的额外字节。

如果你真的需要长尾数,那么尝试使用自定义浮点库是一个选项。 您可以select足够的任何东西,例如,如果只需要40位尾数和7位指数,则更改库以适应您自己的新的48位浮点types。 不需要花费时间计算/存储不必要的16位。 但是这个库应该是非常高效的,因为编译器的库通常对它们自己的floattypes进行汇编级优化。