为什么双精度浮点数比单精度浮点数更受欢迎?

19

在我看到的大多数代码中,即使不需要高精度,double仍然比float更受欢迎。

由于使用双精度类型(CPU / GPU / 内存 / 总线 / 缓存等)存在性能惩罚,那么为什么会过度使用双精度?

例如:在计算流体力学中,我所用的所有软件都使用双精度。在这种情况下,高精度是无用的(因为数学模型中的近似误差),并且有大量数据需要移动,使用浮点数可以减少一半数据量。

今天计算机性能强大无意义,因为它们被用来解决越来越复杂的问题。


3
即使不需要高精度,如果它们使用的是 float,你可能会抱怨说"即使不需要高性能"。 - Karoly Horvath
5
由于绝大多数代码路径都不太关注性能,而且额外的精度不会有害(相反的情况是正确的?) - Matthieu M.
11
根据架构不同,硬件可能(例如x86)仅实现“ double ”并通过转换为“ double ”再转换回“ float ”来模拟“ float ”,这会使其更加昂贵。 - David Rodríguez - dribeas
2
这是一个关于双精度和单精度转换的stackoverflow讨论:https://dev59.com/qGQn5IYBdhLWcg3wj3zz。 - Richard Chambers
3
@DavidRodríguez-dribeas,我理解你说double已经被实现但float没有的意思,但这是不正确的。旧的FPU指令使用80位双扩展数字,既不是float也不是double,但这并不重要:它可以加载和保存浮点数和双精度浮点数,而没有性能惩罚(或许具有讽刺意味的是,加载/保存80位浮点数的指令是慢的)。在非古老的x86系统上,浮点数和双精度浮点数都直接使用SSE(2)实现。 - harold
显示剩余10条评论
6个回答

22

其中包括:

  • 节省的效益很少(数值计算并非典型)。
  • 舍入误差会累积,因此最好从一开始就采用比所需更高的精度(专家可能知道它已经足够精确,而且有些计算可以完全准确地进行)。
  • 常见的浮点运算在内部使用fpu时通常使用双精度或更高精度。
  • C和C++可以隐式地将float转换为double,反之则需要显式强制转换。
  • 可变参数和无原型函数总是得到double,而不是float。(第二个只在古老的C中存在,并且被积极反对)
  • 您通常会使用超过所需精度的操作,但很少使用较少精度的操作,因此库通常也倾向于更高的精度。

但最终,你的情况可能有所不同:要自行衡量、测试和决策。

顺便说一句:对于性能狂热者来说甚至还有更多:使用IEEE半精度类型。虽然很少有硬件或编译器支持,但它可以再次将带宽要求减半。


13
“节省的价值几乎不值得” - 对于单个计算(例如将总和保存在单个变量中)- 没问题。对于获取大量数据- 不行,这会使带宽增加一倍。 - Karoly Horvath
"舍入误差会累积" - 在许多情况下,与其他原因(例如数学模型)导致的误差相比,舍入误差是可以忽略不计的。 - Pietro
@presiuslitelsnoflek - 是的,不总是。但我会说在大多数情况下是这样的。 - Pietro
7
使用更小的尺寸还有另一个原因,那就是不会让你的缓存膨胀。另外一个原因是,很多SSE指令都有双精度和单精度版本,而单精度版本可以在一条指令中操作两倍的数据量。(带宽加倍,乐趣加倍) - Apriori
3
我想补充一点,单精度浮点数的精度限制可能比人们天真地想象的更为常见。在我的OpenGL经验中,我多次不得不从坐标中消除偏差或选择时间周期函数中更短的模数,而不是我本来希望的那样,仅仅是因为GPU的主要部件32位浮点数精度不够。 - Dolda2000
在我的个人生活历史中,我使用浮点数,因为我认为这样会更快,但我在将东西推到短坐标而没有留有余地时遇到了舍入精度问题(通常使用+0.5或round()函数)。改用double后,几乎所有问题都解决了,而且没有性能差异。从那时起,我都是double。然后加入了一家公司,但由于与GPU的缓冲区兼容性原因,所有的数据类型都是float。果然,在可以编辑类似行星尺度的DCCT中遇到了巨大的精度问题。这就像地狱的循环... - v.oddou

12

double在C语言中是一种“自然”的浮点类型,这也影响了C++。请考虑以下事实:

  • 13.9这样的未加修饰的普通浮点常量具有double类型。要使它成为float,我们必须添加额外的后缀fF
  • C中的默认参数提升将float函数参数*转换为double:当没有声明存在于参数时,如函数被声明为可变参数(例如printf)或不存在声明时(旧式C,在C++中不允许)会发生这种情况。
  • printf%f转换说明符接受double参数而不是float。没有专门的方法打印floatfloat参数默认提升为double,因此与%f匹配。

在现代硬件上,floatdouble通常分别映射到32位和64位IEEE 754类型。硬件使用64位值“本地”:浮点寄存器宽度为64位,并且操作是围绕更精确的类型构建的(或者在内部可能比那还要精确)。由于double映射到该类型,因此它是“自然”的浮点类型。

float的精度对于任何严格的数值计算工作来说都很差,并且缩小的范围也可能成为问题。IEEE 32位类型仅具有23位的尾数(8位被指数字段消耗掉,一位用于符号)。如果在给定的应用程序中精度和范围的损失不是问题,则float类型可用于保存大型浮点值数组中的存储。例如,在音频中使用32位浮点值来表示样本。

使用64位类型比32位类型可以将原始内存带宽翻倍,这是真的。然而,这只影响那些具有大量数据数组且访问模式显示出低局部性的程序。64位浮点类型的卓越精度胜过优化问题。根据“先正确再快速”的原则,数值结果的质量比运行时间更重要。


*请注意,不存在从float表达式到double的普遍自动提升;该类唯一的提升是整数提升:charshort和位域转换为int


这个语句有点问题:“硬件原生支持64位值”。SSE/AVX寄存器宽度为128/256位,可以打包浮点数和双精度浮点数,因此两种格式对于硬件来说都是同样本地化的。 - void_ptr

12

在我看来,迄今为止的回答并没有真正传达出正确的观点,因此我来试试。

简短的答案是C++开发者使用double而不是float:

  • 当他们不太了解性能权衡时(“它们具有更高的精度,为什么不用呢?”),避免过早优化。
  • 习惯
  • 文化
  • 匹配库函数签名
  • 匹配容易编写的浮点数文字(您可以编写0.0而不是0.0f)

虽然大多数FPU的内部表示比32位浮点数或64位双精度浮点数表示更宽,因此在单个计算上双精度浮点数可能与单精度浮点数一样快。

但这只是整个情况的一小部分。现在,如果您在缓存/内存带宽上受到限制,则操作优化就毫无意义。

以下是为什么有些开发人员寻求优化其代码应该考虑使用32位浮点数而不是64位双精度浮点数的原因:

  • 它们占用一半的内存。这就像让所有缓存的容量增加一倍。(大胜利!!!)
  • 如果您真的关心性能,您将使用SSE指令。操作浮点值的SSE指令有不同的32位和64位浮点表示法的指令。32位版本可以在128位寄存器操作数中放置4个值,但是64位版本只能放置2个值。在这种情况下,使用float而不是double,由于每个指令都可以处理两倍的数据,因此您可能会将FLOPS加倍。

总的来说,我遇到的大多数开发人员对浮点数如何工作真的缺乏了解。因此,我并不惊讶大多数开发人员盲目地使用double。


7

这主要取决于硬件,但是请注意,最常见的CPU(基于x86 / x87)具有在80位浮点精度上运行的内部FPU(超过浮点数和双倍精度)。

如果您必须在内存中存储一些中间计算,双精度对于内部精度和外部空间来说是不错的平均值。在单个值上,性能更多或少相同。 在大型数字管道上,它可能会受到内存带宽的影响(因为它们将具有双倍长度)。

请注意,浮点数的精度约为6个小数位。 在N立方复杂度问题(例如矩阵反演或变换)中,muldiv会损失两到三个数字,只剩下3个有效数字。在1920像素宽的显示器上,它们根本不够用(您需要至少5个有效数字才能正确匹配一个像素)。

这大致使双精度更可取。


我同意,但有些问题并不需要高精度,重要的是数据大小和传输速度(例如,解决方案稳定的问题)。 - Pietro
“我同意...但是”这样的评论有什么用处呢?(但是“但是”却是否定了前面的“同意”,然后又加入了我也写过的东西?) - Emilio Garavaglia
假设存在两类问题。1)精度至关重要的问题,2)计算时间至关重要的问题。您的回答适用于第一类问题,在这方面我同意您的观点。第二类问题的例子可以是天气预报:如果计算某个时期所需的时间比该时期本身还长,则预报就毫无意义(我会得到昨天的预报)。这是“但是”适用的情况。 - Pietro

4

通常情况下,即使需要进行大量数字分析才能确定float足够使用,但判断double是否足够使用通常相对容易。这样可以节省开发成本,并避免在分析不正确的情况下出现错误结果。

此外,使用float带来的性能提升通常比使用double更微小,因为大多数流行的处理器都使用一种甚至比double更宽的格式进行浮点运算。


3

我认为更高的精度是唯一的原因。实际上,大多数人并不会过多考虑,他们只使用双精度。

我认为如果浮点精度对于特定任务已经足够好了,那么没有理由使用双精度。


我认为你说得非常正确。大多数开发人员似乎真的不太关注(或者说不理解)浮点表示细节及其影响。很多开发人员使用双精度浮点数的原因可能与你在各处看到的隐式int/float float/int转换的原因相似;更多是缺乏理解而非必要性。我认为答案是“这是文化问题”。然而,我认为这个答案的思路可以更加深入,并提供更具体的细节,以便成为可选择/接受的答案。 - Apriori
1
开发人员往往盲目使用“float”,结果遭受精度问题的困扰。 - dan04

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接