使用 2 个“float”模拟“double”

Emulate "double" using 2 "float"s

提问人: 提问时间:7/21/2011 最后编辑:phuclv 更新时间:9/10/2020 访问量:15398

问:

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

使用词典排序应该很简单。然而,添加有点棘手,因为我不确定我应该使用哪个底座。应该是吗?我怎样才能检测到携带?FLT_MAX

这怎么能做到呢?


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

C++ 算法 浮点 精度 双算术

评论

0赞 dan04 7/21/2011
您是否需要额外的范围 ,还是只需要额外的有效数字?double
0赞 7/21/2011
我需要额外的有效数字。
6赞 Rudy Velthuis 7/21/2011
那么你的值范围是多少?指数差异是否很大,有很大的不同。如果它们都在相似的范围内,则可以使用定点数学。特别是如果你只需要加法和比较,定点可能是要走的路。
1赞 7/21/2011
指数差异很大。我在 1e-3、1e+10 和 1e+20 周围几乎有三个不同的间隔。
5赞 Darren Engwirda 7/21/2011
@M.S:这似乎是一个巨大的范围,即使对于精度也是如此。具体来说,并且与 epsilon 的差异更大(因为这通常是左右),所以我希望像这样的操作等同于 ,即使在使用 .这会是一个问题吗??double1.0E+201.0E-03double1.0E-161.0E+20 + 1.0E-031.0E+20double

答:

11赞 Nemo 7/21/2011 #1

这并不简单。

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

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

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

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

评论

5赞 phuclv 5/30/2014
使用浮点数-浮点数技术,他无法实现双倍的范围和精度,但这比浮点数要多得多,而且在只有硬件浮点数的情况下,例如在旧的 CUDA 或 ARM CPU 的情况下,这比软件双倍要快得多
2赞 phkahler 7/21/2011 #2

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

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

评论

2赞 phuclv 12/1/2016
它实际上非常实用,以防万一不需要额外的范围,并且在旧的 NVIDIA CUDA GPU 中广泛使用,当它们不支持双精度时,或者在某些编译器中,当硬件支持不可用时,它被广泛使用以实现接近四倍精度double
7赞 Matthew Slattery 7/21/2011 #3

如果您同时需要精度和宽范围,则需要双精度浮点的软件实现,例如 SoftFloat

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

9赞 Mr Fooz 7/21/2011 #4

这在一定程度上取决于您要执行的操作类型。如果您只关心加法和减法,那么 Kahan Summation 可能是一个很好的解决方案。

评论

1赞 phkahler 7/21/2011
+1 我没听说过的有趣技术。不过,如果他的输入需要额外的精度才能开始(不确定),那将无济于事。
1赞 Don Hatch 11/19/2020
@phkahler 如果求和的输入已经是浮点数-浮点数,只需分别输入两个浮点数即可。
105赞 njuffa 7/21/2011 #5

双浮点数是一种使用单精度数对实现单精度算术精度几乎两倍的技术,同时单精度指数范围略有减小(由于范围远端的中间下溢和溢出)。基本算法是由 T.J. Dekker 和 William Kahan 在 1970 年代开发的。 下面我列出了两篇相当新的论文,展示了这些技术如何适应 GPU,但这些论文中涵盖的大部分材料都独立于平台适用,因此应该对手头的任务有用。

https://hal.archives-ouvertes.fr/hal-00021443纪尧姆·达·格拉萨(Guillaume Da Graça),大卫·德福尔(David Defour) 在图形硬件上实现浮点-浮点运算符, 第 7 届实数与计算机会议,RNC7。

http://andrewthall.org/papers/df64_qf128.pdf安德鲁·索尔 用于 GPU 计算的扩展精度浮点数。

评论

11赞 R.. GitHub STOP HELPING ICE 7/21/2011
+1 与其他答案不同,这个答案实际上解决了 OP 的问题,并提供了指向相关论文的非常好的链接。
0赞 Slava P 8/16/2016
非常好!我只是在寻找一种方法来使我的函数更精确,并且只用了半个小时就实现了第一个环节中的函数。constexpr
5赞 wallyk 7/21/2011 #6

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

一项快速调查显示,Briggs 的 doubledouble C++ 库应该可以满足您的需求,然后是一些。看这个。[*]默认实现基于实现 30 个有效数字的计算,但它很容易被重写以用于实现 13 或 14 个有效数字。如果注意将具有相似量级值的加法运算分开,只在最后的运算中将极值相加,这可能足以满足您的要求。doublefloat

但要注意,评论中提到弄乱 x87 控制寄存器。我没有检查细节,但这可能会使代码太不可移植,无法供您使用。


[*]C++源代码由该文章链接,但只有gzip压缩的tar不是死链接。

2赞 Ioan 7/21/2011 #7

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

评论

0赞 phuclv 2/5/2023
如果 OP 真的不需要任意精度(比如 46 位精度就足够了),那么 MPFR 就太过分了。特别是OP已经说过这是在一些嵌入式硬件上,MPFR可能太大太慢,甚至没有足够的内存来运行。OP 只需要额外的有效数字,而不是额外的范围
3赞 phuclv 8/2/2013 #8

这类似于许多编译器在某些仅具有硬件计算支持的机器上用于精度的双精度算术。它也被用作不支持的旧 NVIDIA GPU 上的浮点数。请参阅在 GPU 上使用 2 个 FP32 模拟 FP64。这样,计算将比软件浮点库快得多。doubledouble

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

If you really need the longer mantissa, try using a custom floating-point library. You can choose whatever is enough for you, for example change the library to adapt a new 48-bit float type of your own if only 40 bits of mantissa and 7 bits of exponent is needed. No need to spend time for calculating/storing the unnecessary 16 bits anymore. But this library should be very efficient because compiler's libraries often have assembly level optimization for their own type of float.