为什么双重优先于浮动?

Pie*_*tro 17 c++ floating-point double performance

在我看到的大多数代码doublefloat,即使不需要高精度也是最受欢迎的代码.

由于使用双重类型(CPU/GPU /内存/总线/缓存/ ...)时存在性能损失,这种双重过度使用的原因是什么?

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

今天的计算机功能强大这一事实毫无意义,因为它们被用来解决越来越复杂的问题.

Ded*_*tor 22

其中:

  • 节省的费用几乎不值得(数字运算不典型).
  • 舍入误差累积,因此从一开始就更好地达到比所需更高的精度(专家可能知道它无论如何都足够精确,并且有可以精确完成的计算).
  • 在内部使用fpu的常见浮动操作通常在双精度或更高精度上工作.
  • C和C++可以隐式地从float转换为double,另一种方式需要显式转换.
  • Variadic和no-prototype函数总是得到两倍,而不是浮点数.(第二个只在古代C中,并积极劝阻)
  • 您通常可以以超过所需的精度执行操作,但很少用较少的操作,因此库通常也支持更高的精度.

但最后,YMMV:测量,测试并自行决定您的具体情况.

BTW:性能狂热分子还有更多:使用IEEE半精度类型.存在很少的硬件或编译器支持,但它再次将您的带宽需求减半.

  • "节省的费用几乎不值得" - 对于单次计算(比如在一个变量中保存总和) - 当然.为了获取大量数据 - 不,你加倍了带宽. (11认同)
  • 是的,不要膨胀您的缓存是使用较小尺寸的另一个原因.另一个是许多SSE指令有double和float版本,而float版本在一条指令中运行的数据是两倍.(加倍你的带宽加倍你的乐趣) (7认同)
  • 我只想补充一点,即单浮标的精度受到的限制可能比人们天真的认为的要多得多。在我使用OpenGL的经验中,我不得不几次不得不消除坐标偏差或在时间周期函数上选择比我所希望的短的模数,这仅仅是因为我在作为32位浮点数的主要部分的精度上已经用尽了GPU。 (3认同)

Kaz*_*Kaz 11

double在某种程度上,C语言中的"自然"浮点类型也会影响C++.考虑一下:

  • 一个朴素的,普通的浮点常数,就像13.9有类型一样double.为了使它浮动,我们必须添加一个额外的后缀fF.
  • C中的默认参数提升float函数参数*转换为double:当没有参数声明时,例如当函数声明为variadic(例如printf)或者没有声明存在时(旧式C,C++中不允许).
  • %f转换符printf需要一个double说法,不是float.没有专门的打印方式float; 一个float参数默认提升到double等相匹配%f.

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

float任何严肃的数字工作的精度都很差,减小的范围也可能是一个问题.IEEE 32位类型只有23位尾数(指数字段消耗8位,符号消耗1位).float类型对于在大型浮点值数组中保存存储非常有用,前提是精度和范围的损失在给定的应用程序中不是问题.例如,有时在音频中使用32位浮点值来表示样本.

确实,使用32位类型的64位类型会使原始内存带宽加倍.但是,这只影响具有大量数据的程序,这些程序以显示不良局部性的模式访问.64位浮点类型的卓越精度胜过优化问题.数值结果的质量比剃须周期的运行时间更重要,按照"先把它做,然后再做快"的原则.


*但请注意,float表达式中没有一般的自动升级double; 唯一的推广之类的是积分的推广:char,short与位域去int.


Apr*_*ori 9

在我看来,到目前为止的答案并没有真正得到正确的观点,所以这是我的解决方案.

简短的回答是C++开发人员在浮点数上使用双精度数:

  • 当他们不能很好地理解性能权衡时避免过早优化("他们有更高的精度,为什么不呢?"是思想过程)
  • 习惯
  • 文化
  • 匹配库函数签名
  • 匹配简单到写的浮点文字(你可以写0.0而不是0.0f)

对于单个计算,它的真实双倍可能与浮点一样快,因为大多数FPU具有比32位浮点或64位双重表示更宽的内部表示.

然而,这只是图片的一小部分.如果你的缓存/内存带宽瓶颈,现在的运营优化并不意味着什么.

这就是为什么一些寻求优化代码的开发人员应该考虑使用32位浮点数而不是64位双精度数:

  • 它们适合记忆的一半.这就像让你的所有缓存都大两倍.(大赢!!!)
  • 如果您真的关心性能,那么您将使用SSE指令.对浮点值进行操作的SSE指令对32位和64位浮点表示具有不同的指令.32位版本可以在128位寄存器操作数中容纳4个值,但64位版本只能容纳2个值.在这种情况下,您可以通过使用浮点数超过双倍来使FLOPS加倍,因为每条指令的运行数据是两倍.

一般来说,在我遇到的大多数开发人员中,实际上缺乏关于浮点数如何真正起作用的知识.所以我并不感到惊讶,大多数开发人员盲目地使用双倍.


Emi*_*lia 7

这主要取决于硬件,但考虑到最常见的CPU(基于x86/x87)具有内部FPU,它以80位浮点精度(超过浮点数和双精度数)运行.

如果你必须在内存中存储一​​些中间计算,double是内部精度和外部空间的良好平均值.在单个值上,性能或多或少相同.它可能受到大型数字管道上的内存带宽的影响(因为它们具有双倍长度).

考虑浮点数的精度约为6位十进制数.在N立方复杂性问题(如矩阵求逆或变换)上,你会丢失两到三个,mul并且div只剩下3个有意义的数字.在1920像素宽的显示器上,它们是不够的(您需要至少5个才能正确匹配像素).

这大致是双倍的优选.