【问题标题】:Physics engine: use double or single precision?物理引擎:使用双精度还是单精度?
【发布时间】:2013-12-24 06:47:18
【问题描述】:

我正在从头开始制作一个刚体物理引擎(用于教育目的),我想知道是否应该为其选择单精度或双精度浮点数。

我将使用 OpenGL 对其进行可视化,并使用 glm 库在引擎内部计算内容以及用于可视化。约定似乎是在几乎所有地方都使用浮点数用于OpenGL,glm::vec3glm::vec4 似乎在内部使用float。我还注意到虽然有glm::dvec3glm::dvec4,但似乎没有人使用它。我如何决定使用哪个? double 似乎很有意义,因为它在当今的硬件上具有更高的精度和几乎相同的性能(据我所知),但其他一切似乎都使用 float except 用于某些 GLu函数和一些 GLFW 的。

【问题讨论】:

  • 我的直觉告诉我float 如果你做对了就足够了。期待一个实际的答案:)
  • 你想多了……
  • 你可以使用模板...
  • 在物理上使用 double 并在渲染中使用 float 是否可行?
  • 如果您想在大于一公里左右的世界上使用物理引擎,请使用 double

标签: c++ opengl floating-point simulation floating-accuracy


【解决方案1】:

这一切都将取决于您的应用程序。您几乎已经了解两者之间的权衡:

单精度

  • 不太准确
  • 即使在当今的硬件上也能实现更快的计算。占用内存更少,操作更快。从缓存优化等中获得更多收益。

双精度

  • 更准确
  • 计算速度较慢。

通常在图形应用程序中,考虑到屏幕上的像素数量和场景的缩放比例,浮点数的精度非常高。在科学设置或更小规模的模拟中,您可能需要额外的精度。它也可能取决于您的硬件。例如,我为上网本上的刚体编写了一个基于物理的模拟,然后切换到浮动,平均获得 10-15 FPS,这在我的实现中几乎翻了一番。

我的建议是,如果这是一项教育活动,请使用浮动并以图形应用程序为目标。如果您在学习、时间和个人经验中发现您需要双精度,那么就朝着这个方向前进。

【讨论】:

  • 一个可靠的答案。我还要补充一点,如果 OP 对模板足够熟悉,他可能会模板化类型。对于物理方面,这应该可以很好地使用 float 或 double 使用而无需修改代码。将其发送到 OGL 可能需要更多的工作,因为您必须检测输入类型是浮点数还是双精度并相应地进行转换。不过,这可以通过模板专业化来完成。
  • @djechlin 实际上并非如此。 OP 假设 float 和 double 操作的成本相同。他们在内存或cpu时间方面没有。这也增加了依赖于应用程序的知识。
  • 它必须足以制作某种可玩的迷你游戏,但我想浮动就足够了。所以如果我从glfwGetTime 获得时间,我基本上应该立即将它从double 转换为float
  • 你可以的。我不会太担心 GetTime 之类的事情,您的应用程序的瓶颈可能是冲突解决、绘图和集成,而不是时间。确保您与昂贵的操作保持一致并打开 GL 项目。
  • 我只是觉得使用float dt 在某种程度上是不准确的。但我想这只是一些武断的想法。但我刚刚意识到,我几乎在所有地方都使用int,而不是long,这很相似。
【解决方案2】:

这是short answer

Q. Why does OpenGL use float rather than double? 
A. Because most of the time you don't need the precision and doubles 
are twice the size.

要考虑的另一件事是,您不应该在任何地方都使用双精度数,就像某些事情可能需要使用双精度数而不是浮点数一样。例如,如果您通过循环角绘制正方形来绘制圆形,则屏幕上只能显示这么多的正方形。它们会重叠,在这种情况下,双打毫无意义。但是,如果您正在执行任意浮点运算,如果您试图准确地表示 Mandelbrot 级数,则可能需要额外的精度(尽管这完全取决于您的算法。)

无论哪种方式,最后,如果您打算在绘图中使用这些值,您通常需要将这些值转换回浮点数。

【讨论】:

  • 更不用说直到最近消费级 GPU 硬件甚至没有双精度功能。那些仍然没有在整个管道中公开双精度的。因此,您从双精度中受益的唯一地方是在 CPU 上完成的计算,而这是当今实现物理的一种非常不受欢迎的方式;确保他们有 CPU 回退,但首选路径是通过 CUDA、OpenCL 或其他一些 GPGPU 解决方案。
  • @Andon 是的,现在人们只是为所有东西制作着色器。我已经看到了一些 mandelbrot 的 HLSL 实现,但到目前为止它们只能放大。我认为在大多数情况下,一切仍在 CPU 上完成。
【解决方案3】:

单次 prec 操作速度更快,数据使用的内存更少,网络带宽也更少。因此,只有在获得某些东西以换取较慢的操作以及所需的更多内存和带宽时,才使用 double 。当然,在刚体物理的某些应用中,额外的精度是值得的,例如在操纵纬度\经度时,单精度只能为您提供米级精度,但这是您的情况吗?

由于它是教育目的,也许您想自学使用高精度物理算法,其中额外的准确性很重要,但许多刚体物理涉及只能近似量化的过程,例如 2 个固体之间的摩擦、碰撞检测后的反应等,额外的精度并不重要,你只会得到更精确的近似行为:)

【讨论】:

  • >但是许多刚体物理涉及只能近似量化的过程,例如两个固体之间的摩擦,这是不正确的!有用于多体接触的求解器,如果这太不精确,可以退回到使用“简化坐标方法”,请参阅第 428 页的“游戏物理引擎开发”一书。基于能量/守恒原理,您必须做许多偏导数和为每个特殊的约束情况计算一个简单的公式。可悲的是,这只是在文献中被划伤而无处使用?
【解决方案4】:

当然,一般规则是正确性第一,性能第二?这意味着使用doubles,除非您可以说服自己使用floats 可以获得所需的保真度。

要看的是坐标系相对于您要建模的最小尺寸的有效尺寸。

例如,如果您使用地球坐标,则 100 度大约是 1E7 米。 IEEE 754 浮点数只有 23 位精度,因此相对精度仅为 1E-7 左右。 因此,坐标仅精确到 1 米左右。这可能足以解决问题,也可能不够。

我从经验中了解到始终使用doubles 进行物理和物理建模计算,但承认这不是普遍要求。

当然并不意味着渲染应该使用double;您可能希望将其作为float

【讨论】:

    【解决方案5】:

    我在一个通用标题中使用了 typedef,并使用 float 作为我的默认值。

    typedef real_t float;
    

    我不建议为此使用模板,因为当您尝试使用多态/虚拟函数时,它会导致巨大的设计问题。

    为什么浮动有效

    花车对我来说效果很好,原因有 3 个:

    首先,几乎每个物理模拟都需要在力和扭矩上添加一些噪音以实现真实感。这种随机噪声的幅度通常远大于浮点数的精度。

    其次,在许多情况下,有限的精度实际上是有益的。考虑到几乎所有关于刚体的经典力学都不适用于现实世界,因为没有完美的刚体。因此,当您对不完美的刚体施加力时,您不会获得完美的加速度到第 7 位。

    第三,许多模拟的持续时间很短,因此累积的误差仍然足够小。使用双精度不会自动改变这一点。创建与现实世界相匹配的长时间运行的模拟非常困难,并且将是非常专业的项目。

    当浮动不起作用时

    这是我不得不考虑使用双精度的情况。

    1. 纬度和经度应该是两倍。对于这些数量,对于大多数用途来说,浮点数根本就没有足够好的分辨率。
    2. 随着时间的推移计算非常少量的积分。例如,高斯马尔可夫过程是表示传感器偏差中随机游走的好方法。然而,这些值通常会非常小并且会累积。浮点数的计算错误可能比双精度数大得多。
    3. 超越通常的刚体线性和旋转运动经典力学的专业模拟。例如,如果您使用蛋白质分子、晶体生长、微重力物理等进行操作,那么您可能想要使用 double。

    当双打不起作用时

    实际上,双倍伤害的更高精确度有时会受到伤害,尽管这种情况很少见。 What every computer scientists should know... 的一个例子:如果你有一些随着时间的推移收敛到 1 的数量。如果结果为 0,则获取它的日志并执行某些操作。使用 double 时,您可能永远不会达到 1,因为可能不会发生舍入,但使用浮点数可能会发生。

    另一个例子:您需要使用special code 来比较实际值。这些代码通常具有默认舍入到 epsilon,对于浮点数来说是相当合理的 1E-6,但对于其 1E-15 来说是两倍。如果您不小心,这可能会给您带来很多惊喜。

    性能

    还有一个惊喜:在现代 x86 硬件上,float 与 double 的原始性能几乎没有区别。内存对齐、缓存等几乎比浮点类型占主导地位。在我的机器上,对 100M 随机数和浮点数的简单求和测试耗时 22 秒,双倍数需要 25 秒。所以浮点数确实快了 12%,但我仍然认为它太低而不能仅仅为了性能而放弃双精度。但是,如果您使用 SSE 指令或 GPU 或 Arduino 等嵌入式/移动硬件,那么浮点数会更快,这肯定是驱动因素。

    除了刚体的线性和旋转运动之外什么都不做的物理引擎可以在当今的桌面级硬件上以 2000Hz 的频率在单线程上运行。您可以轻松地将其并行化到许多内核。许多简单的低端模拟只需要 50Hz。在 100Hz 时,事情开始变得非常顺利。如果你有 PID 控制器之类的东西,你可能必须提高到 500Hz。但即使在这种最差情况下,您仍然可以使用足够好的桌面模拟大量对象。

    总之,除非您实际衡量它,否则不要让性能成为您的驱动因素。

    做什么

    经验法则是使用尽可能多的精度来使代码正常工作。对于刚体的简单物理引擎,浮动通常就足够了。但是,您希望能够在不修改代码的情况下改变主意。因此,最好的方法是使用开头提到的 typedef,并确保您的代码既适用于浮点数,也适用于双精度数。然后经常测量并随着项目的发展选择类型。

    在您的案例中,另一件重要的事情是:保持物理引擎与渲染系统完全分离。物理引擎的输出可以是双精度或浮点型,并且应该根据渲染系统的需要进行类型转换。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-02-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-09
      • 2010-11-07
      • 1970-01-01
      相关资源
      最近更新 更多