为什么双倍优先于浮点数
Why are double preferred over float?
在我看到的大多数代码中,double
是float
最喜欢的,即使不需要高精度。
类型(CPU/GPU/内存/总线/缓存/...)时会有性能损失,这种双重过度使用的原因是什么?
示例:在计算流体动力学中,我使用的所有软件都使用双精度。在这种情况下,高精度是无用的(因为数学模型中的近似误差),并且有大量数据需要移动,可以使用浮点数将其切成两半。
今天的计算机功能强大这一事实毫无意义,因为它们被用来解决越来越复杂的问题。
其中包括:
- 节省的成本几乎不值得(数字处理并不典型)。
- 舍入误差会累积,因此最好从一开始就达到比需要更高的精度(专家可能知道它无论如何都足够精确,并且有一些计算可以精确完成)。
- 无论如何,在内部使用 fpu 的常见浮动操作通常以双倍或更高的精度工作。
- C 和 C++ 可以从浮点隐式转换为双精度,另一种方式需要显式强制转换。
- 可变参数和无原型函数总是获得双精度,而不是浮点数。(第二个只在古代C,积极劝阻)
- 您通常可能会以超过所需精度执行操作,但很少使用更少的精度,因此库通常也倾向于更高的精度。
但最后,YMMV:为自己和您的具体情况测量、测试和决定。
顺便说一句:对于性能狂热者来说,还有更多:使用IEEE半精度类型。对它的硬件或编译器支持很少,但它再次将您的带宽需求减少了一半。
在我看来,到目前为止的答案并没有真正得到正确的观点,所以这是我的破解。
简短的回答是C++开发人员使用双精度而不是浮点数:
- 避免在他们
- 不太了解性能权衡时过早优化("他们有更高的精度,为什么不呢?是思维过程)
- 习惯
- 文化
- 匹配库函数签名
- 匹配易于编写的浮点文本(可以写入 0.0 而不是 0.0f)
对于单个计算,双精度可能与浮点数一样快,因为大多数 FPU 的内部表示比 32 位浮点数或 64 位双精度表示更宽。
然而,这只是图片的一小部分。现在的操作优化并不意味着什么,如果你在缓存/内存带宽上遇到瓶颈。
这就是为什么一些寻求优化代码的开发人员应该考虑使用 32 位浮点数而不是 64 位双精度数:
- 它们占了一半的记忆。这就像让所有缓存都是原来的两倍。(大赢!!)
- 如果您真的关心性能,您将使用 SSE 指令。对浮点值进行操作的 SSE 指令对 32 位和 64 位浮点表示具有不同的指令。32 位版本在 128 位寄存器操作数中可以容纳 4 个值,但 64 位版本只能容纳 2 个值。在这种情况下,您可以通过使用浮点数而不是双倍来使 FLOPS 加倍,因为每条指令对两倍的数据进行操作。
总的来说,在我遇到的大多数开发人员中,确实缺乏对浮点数如何真正工作的了解。因此,我对大多数开发人员盲目使用double并不感到惊讶。
double
在某些方面是C语言中的"自然"浮点类型,这也影响了C++。考虑一下:
- 像
13.9
这样朴素的普通浮点常量具有double
型。要使其浮动,我们必须添加一个额外的后缀f
或F
. - C 中的默认参数提升将函数参数
float
转换为double
:当参数不存在声明时,例如当函数声明为可变参数时(例如printf
)或不存在声明(旧样式 C,C++ 中不允许)。 printf
的%f
转换说明符采用double
参数,而不是float
。 没有专门的方法来打印float
-s;一个float
参数默认提升为double
,因此与%f
匹配。
在现代硬件上,float
和double
通常分别映射到 32 位和 64 位 IEEE 754 类型。硬件"本机"处理 64 位值:浮点寄存器宽 64 位,操作围绕更精确的类型构建(或者内部可能比这更精确)。 由于double
映射到该类型,因此它是"自然"浮点类型。
对于任何严肃的数值工作来说,float
的精度都很差,而且缩小的范围也可能是一个问题。IEEE 32 位类型只有 23 位尾数(8 位由指数字段消耗,1 位用于符号)。浮点类型对于在浮点值的大型数组中保存存储非常有用,前提是精度和范围的损失在给定应用程序中不是问题。例如,音频中有时会使用 32 位浮点值来表示样本。
确实,使用 64 位类型而不是 32 位类型会使原始内存带宽加倍。但是,这只会影响具有大量数据数组的程序,这些数据以显示较差局部性的模式进行访问。64位浮点型的卓越精度胜过优化问题。数值结果的质量比按照"先做对,再做快"的原则,缩短运行周期更重要。
* 但是请注意,从float
表达式到double
没有一般的自动提升;这种唯一的提升是积分提升:char
、short
和位域将int
。
这主要取决于硬件,但考虑到最常见的 CPU(基于 x86/x87)具有内部 FPU,该 FPU 以 80 位浮点精度运行(超过浮点数和双精度数)。
如果你必须在内存中存储一些中间计算,双倍是内部精度和外部空间的良好平均值。在单个值上,性能或多或少是相同的。它可能会受到大型数字管道上的内存带宽的影响(因为它们将具有双倍长度)。
考虑到浮点数的精度约为 6 位十进制数字。在 N 立方复杂度问题(如矩阵反演或变换)上,您在mul
和div
中又损失了两到三个,只剩下 3 个有意义的数字。在 1920 像素宽的显示器上,它们根本不够(您至少需要 5 个才能正确匹配一个像素)。
这大致使双倍更可取。
确定双精度值就足够了通常相对容易,即使在需要大量的数值分析工作才能证明浮点数就足够的情况下也是如此。这样可以节省开发成本,并在分析不正确的情况下降低结果不正确的风险。
此外,使用浮点数的任何性能提升通常比使用双精度值要小,这是因为大多数流行的处理器都以一种比双精度更宽的格式执行所有浮点运算。
我认为更高的精度是唯一的原因。其实大多数人并没有想太多,他们只是用双倍。
我认为如果浮点精度对于特定任务来说足够好,就没有理由使用双精度。
- 在C++中,将大的无符号浮点数四舍五入为整数的最佳方法是什么
- 如何修复此错误:未定义对"距离(浮点数,浮点数,浮点数,浮点数,浮点数)"的引用
- C++浮点数据类型和字符串数据类型无法子到模板函数中
- 使用提升将数据从 PyObject 复制到浮点数 *
- 使用浮点数和双精度数的非常小数字的数学
- 使用英特尔内联函数将打包的 8 位整数乘以浮点数向量
- 如何在 c++ 中将小数点后两位数的浮点数分配给另一个浮点数
- 返回浮点数的小数位数
- txt 文件中浮点数的最大和最小值
- 为什么 std::cout 打印浮点数、双精度和长双精度到相同的小数精度?
- 将浮点数转换为无符号字符数组并打印出来
- 如何将时间字符串 (M:SS) 转换为浮点数
- 如何将浮点数(*)[6]转换为浮点**类型?
- C++将浮点数乘以分数
- 如何将浮点数转换为uint8_t?
- 将字符串转换为浮点数或整数,而无需使用内置函数(如 atoi 或 atof)
- 如何在C++(Arduino)中将浮点数组转换为字节数组
- 在数学上将浮点数四舍五入到 N 位小数
- Cython通过浮点数的最快方式,用于高频控制回路
- 复制 -nan 表示浮点数,AVX __m256 复制后显示 0