【问题标题】:double or float, which is faster? [duplicate]double 或 float,哪个更快? [复制]
【发布时间】:2011-06-02 20:20:57
【问题描述】:

我正在阅读“加速 C++”。我发现有一句话说“有时double 的执行速度比 C++ 中的float 快”。读完句子后,我对floatdouble 的工作感到困惑。请向我解释这一点。

【问题讨论】:

  • @Devendra:那是 C#,不是 C++。
  • 如果您正在阅读“加速 C++”,那么您最不应该担心的是哪种类型更快 - 专注于概念,当您遇到真正的问题时,再担心...
  • @Hippo:你确定语言有影响吗?
  • 浮点范围是 '1.175494351 E - 38' 到 '3.402823466 E + 38' 而双精度范围是 '2.2250738585072014 E - 308' 到 '1.7976931348623158 E + 308'。随后尺寸和长度相应变化。它与使用的语言无关。

标签: c++ floating-point double


【解决方案1】:

您可以在这篇文章中找到完整的答案:

What Every Computer Scientist Should Know About Floating-Point Arithmetic

这是来自之前 Stack Overflow 线程的引述,关于 floatdouble 变量如何影响内存带宽:

如果双重要求 比浮点数更多的存储空间,那么它 读取数据需要更长的时间。 这是天真的答案。在现代 IA32,这一切都取决于数据在哪里 来自。如果它在 L1 缓存中, 负载可以忽略不计,前提是 数据来自单个缓存行。 如果它跨越多个缓存行 开销很小。如果是从 L2,需要一段时间,如果是 在 RAM 中,它仍然更长,并且 最后,如果它在磁盘上,它是一个巨大的 时间。所以选择float还是double 不如数据的方式重要 用来。如果你想做一个小 大量顺序计算 数据,小数据类型更可取。 做很多计算在一个小 数据集将允许您使用更大的 任何重要的数据类型 影响。如果您正在访问数据 非常随机,那么数据的选择 大小不重要 - 数据已加载 在页面/缓存行中。所以即使你 只想要 RAM 中的一个字节,你可以 传输 32 个字节(这非常 依赖于架构的 系统)。最重要的是, CPU/FPU 可以是超标量(又名 流水线)。因此,即使负载可能 需要几个周期,CPU/FPU 可以 忙于做其他事情(a 乘以例如)隐藏 加载时间到一定程度

【讨论】:

    【解决方案2】:

    当双精度数比浮点数快时,我可以想到两种基本情况:

    1. 您的硬件支持双精度运算但不支持​​浮点运算,因此浮点数将由软件模拟,因此速度较慢。

    2. 你真的需要双打的精度。现在,如果你仍然使用浮点数,你将不得不使用两个浮点数来达到与双精度相似的精度。用浮点数模拟真正的双精度会比一开始就使用浮点数要慢。

      1. 您不一定需要双精度数,但由于双精度数的提高,您的数值算法收敛速度更快。此外,双精度数可能会提供足够的精度来使用更快但数值不太稳定的算法。

    为了完整起见,我还给出了浮点数更快的相反情况的一些原因。您可以自己查看哪些原因在您的案例中占主导地位:

    1. 当你不需要双精度时,浮点数比双精度更快 精度,你是内存带宽限制和你的硬件 不会对浮点数进行惩罚。

    2. 它们节省了内存带宽,因为它们占用了一半的空间 每个数字。

    3. 还有一些平台可以处理比双精度更多的浮点数 并行。

    【讨论】:

    • 因为我反复收到未评论的反对票,所以我决定修改我的答案。新的东西在答案的第一部分。
    【解决方案3】:

    float 通常更快。 double 提供更高的精度。但是,如果使用 3dNow 或 SSE 等特殊处理器扩展,在某些情况下性能可能会有所不同。

    【讨论】:

    • 如果您可以使用 SIMD(如 SSE 或其他),float 肯定会更快:每条指令的工作量更多,缓存占用空间更小/内存带宽更低。
    【解决方案4】:

    取决于本机硬件的功能。

    • 如果硬件是(或类似)具有传统 x87 数学的 x86,float 和 double 都(免费)扩展为内部 80 位格式,因此两者具有相同的性能(缓存占用/内存带宽除外)

    • 如果硬件像大多数现代 ISA(包括 x86-64,其中 SSE2 是标量 FP 数学的默认值)一样本机实现两者,那么通常大多数 FPU 操作对两者的速度相同。 Double division and sqrt can be slower than float,当然也比乘法或加法慢得多。 (浮点数越小意味着缓存未命中越少。使用 SIMD,每个向量的元素数量是向量化循环的两倍)。

    • 如果硬件只实现了双精度,那么如果在浮点加载和浮点存储指令中与本机双精度格式之间的转换不是免费的,那么浮点会变慢。

    • 如果硬件只实现浮点,那么用它模拟双精度将花费更多时间。在这种情况下,浮动会更快。

    • 如果硬件都没有实现,那么两者都必须在软件中实现。在这种情况下,两者都会很慢,但 double 会稍微慢一些(至少有更多的加载和存储操作)。

    您提到的引用可能是指 x86 平台,其中给出了第一个案例 。但这通常并不成立。

    还要注意 x * 3.3 + y for float x,y 将触发提升为双变量。这不是硬件的问题,您应该通过编写 3.3f 来避免这种情况,让您的编译器生成高效的 asm,如果您想要的话,实际上将数字保持为浮点数。

    【讨论】:

    • AFAIK x86 实际上有 80 位寄存器,不是浮点数也不是双精度数。
    • 即使在 x86 上,也不是那么简单。旧的 x87 FPU 在内部使用 80 位寄存器,这意味着 both 浮点数和双精度数都需要转换。但如果你使用 SSE/SSE2,CPU 内部不再使用 80 位精度,因此浮点数和双精度数都以其本机精度计算。
    • 您是否可以实际使用 80 位扩展寄存器取决于您的操作系统(Windows 专门让您跳过一些障碍)。我建议忘记这方面,并根据其他标准选择数据类型,例如:您实际需要什么精度?将其实现细节留给编译器和优化器,除非您有真正充分的理由自己手动优化这些东西。 (我唯一需要做的就是在嵌入式硬件上进行速度优化的 FFT)。
    • 关于 80 位支持的附录:“英特尔在 2000 年底推出 P4 时开始不鼓励使用 x87。自 2003 年 K8 以来,AMD 弃用了 x87,因为 x86-64 定义为支持 SSE2 ; 自 2005 年以来,威盛的 C7 已支持 SSE2。在 64 位版本的 Windows 中,x87 已被弃用用于用户模式,并且完全禁止在内核模式中使用。”引用自realworldtech.com/physx87/4
    • @foo: x86-64 有 SIMD float (SSE) 和 double (SSE2)。 SSE2 是 x86-64 的基线。现代 x86 CPU 的 SIMD 具有相同的每个向量的浮点或双加/mul/FMA 性能(因此,float 的 FLOPS 是两倍,因为每个向量有两倍的元素)。 Mysscial 在How do I achieve the theoretical maximum of 4 FLOPs per cycle? 上有详细解答。 double 除法/sqrt 比 float Floating point division vs floating point multiplication
    【解决方案5】:

    在 Intel 上,协处理器(现在已集成)可以同样快速地处理两者,但正如其他一些人所指出的那样,双倍会导致更高的内存带宽,这可能会导致瓶颈。如果您使用的是标量 SSE 指令(大多数 64 位编译器的默认设置),同样适用。所以一般来说,除非您处理大量数据,否则这并不重要。

    但是,并行 SSE 指令将允许在一条指令中处理四个浮点数,但只能处理两个双精度数,因此这里的浮点数可以明显更快。

    【讨论】:

      【解决方案6】:

      简短的回答是:视情况而定

      带有 x87 的 CPU 将同样快速地处理浮点数和双精度数。矢量化代码在使用浮点数时会运行得更快,因为 SSE 可以一次处理 4 个浮点数或 2 个双精度数。

      要考虑的另一件事是内存速度。根据您的算法,您的 CPU 在等待数据时可能会闲置很多。内存密集型代码将受益于使用浮点数,但 ALU 受限代码不会(除非它是矢量化的)。

      【讨论】:

        【解决方案7】:

        32 位浮点数比 64 位双精度浮点数(或 80 位 80x87)慢只有一个原因。这就是对齐。除此之外,浮点数占用更少的内存,通常意味着更快的访问,更好的缓存性能。处理 32 位指令所需的周期也更少。即使(协)处理器没有 32 位指令,它也可以在 64 位寄存器上以相同的速度执行它们。可能可以创建一个测试用例,其中双精度数比浮点数和 v.v. 更快,但我对真实统计算法的测量没有显示出明显的差异。

        【讨论】:

        • 您似乎认为内存访问不会花费任何时间。但根据我的经验(以及我见过的所有硬件的数据表),确实如此。
        • 80 位 x87 加载/存储明显慢于 32 位或 64 位浮点/双精度 fld / fstp。例如Skylake fstp tbyte 是 7 uop,吞吐量为每 5 个周期 1 次,而普通浮点/双精度存储为 1 uop 和每时钟 1 次。见this answer for more
        【解决方案8】:

        在2000000000次加3.3的实验中,结果是:

        Summation time in s: 2.82 summed value: 6.71089e+07 // float
        Summation time in s: 2.78585 summed value: 6.6e+09 // double
        Summation time in s: 2.76812 summed value: 6.6e+09 // long double
        

        所以 double 在 C 和 C++ 中更快并且是默认值。它更具可移植性,并且是所有 C 和 C++ 库函数的默认设置。 Alos double 的精度明显高于 float。

        甚至 Stroustrup 都推荐 double over float:

        “单精度,双精度和扩展精度的确切含义是实现定义的。为选择很重要的问题选择正确的精度需要对浮点计算有深刻的理解。如果你没有那个理解,获得建议,花时间学习,或使用双倍并希望最好。”

        也许您应该使用 float 而不是 double 的唯一情况是在具有现代 gcc 的 64 位硬件上。因为浮动更小; double 是 8 个字节,float 是 4 个字节。

        【讨论】:

        • 那么让我们一起期待吧
        • Double 具有更高的浮点精度,并使用更多内存双 8 字节和浮点 4 字节。最快的是通过内存写入浮动。我不知道你的测试是什么样子的,但是时间很吵。
        • 这个实验需要在我们所有的机器上运行,因为其他答案表明它依赖于硬件。
        • 另外,您应该考虑使用假设检验来证明差异具有统计显着性。 (这个顺序会一直一样吗?如果你重新运行它,你会得到相同的时间吗?)
        • 我怀疑您的 CPU 没有达到float 测试的全时钟速度,可能是因为您没有在基准测试中包含任何“热身”。 Idiomatic way of performance evaluation?float 应该与在 x86 或 ARM 上的普通 C++ 实现上的 double 相同的速度。除非您做错了并且以必须转换为双精度并返回的方式执行浮点版本,因为在 C++ 中3.3 是双精度常量,与3.3f 不同。但如果是这样的话,你会期待更大的放缓。
        猜你喜欢
        • 1970-01-01
        • 2023-03-04
        • 1970-01-01
        • 2012-11-26
        • 1970-01-01
        • 1970-01-01
        • 2011-06-13
        • 1970-01-01
        • 2011-03-26
        相关资源
        最近更新 更多