物理引擎:使用双精度还是单精度?

时间:2013-12-06 19:27:59

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

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

我将使用OpenGL对其进行可视化,并使用glm库来计算引擎内部以及可视化内容。惯例似乎是在任何地方几乎都使用浮动OpenGL,glm::vec3glm::vec4似乎在内部使用float。我也注意到有glm::dvec3glm::dvec4,但似乎没有人使用它。我该如何决定使用哪个? double似乎有意义,因为它在今天的硬件上具有更高的精度和几乎相同的性能(据我所知),但其他一切似乎都使用float ,除了对于GLu的一些功能和GLFW的一些功能。

5 个答案:

答案 0 :(得分:9)

这一切都取决于您的申请。你几乎已经理解了两者之间的权衡:

单精度

  • 不太准确
  • 即使在今天的硬件上也能加快计算速度。占用更少的内存,操作更快。从缓存优化等中获取更多信息

双精度

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

通常在图形应用程序中,浮动的精度很高,因为屏幕上的像素数和场景的缩放比例。在科学设置或较小规模的模拟中,您可能需要额外的精度。它也可能取决于您的硬件。例如,我为上网本上的刚体编码了一个基于物理的模拟,并在平均10-15 FPS时切换到浮点数,这在我的实现中几乎是FPS的两倍。

我的建议是,如果这是一项教育活动,请使用浮动并定位图形应用程序。如果你在学习中找到时间和个人经验,你需要双精度,然后朝这个方向前进。

答案 1 :(得分:5)

一般来说,一般规则是正确性和性能第二吗?这意味着使用double,除非你能说服自己使用float来获得保真度。

要注意的是坐标系相对于您打算建模的最小尺寸的一位有效尺寸。

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

我从经验中学到了总是使用double来进行物理和物理建模计算,但承认这不是普遍的要求。

当然,渲染应该使用double;您可能希望将其作为float

答案 2 :(得分: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.

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

无论哪种方式,最后,如果你打算在绘图中使用这些值,你通常需要强制转换为浮动。

答案 3 :(得分:2)

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

typedef real_t float;

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

为什么花车可行?

浮游物对我来说非常好,有三个原因:

首先,几乎所有的物理模拟都会在力和扭矩上添加一些噪音,使其变得切合实际。这种随机噪声的幅度通常远大于浮子的精度。

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

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

当花车不起作用时

以下是我不得不考虑使用double的情况。

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

    实际上,双重伤害的精度更高,尽管很少见。来自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,并确保你的代码适用于float和double。然后经常测量,并在项目发展时选择类型。

    在你的情况下另一个重要的事情:保持物理引擎与渲染系统分离。物理引擎的输出可以是double或float,也应该根据渲染系统的需要进行类型转换。

答案 4 :(得分:0)

单个预操作更快,数据使用更少的内存减去网络带宽。因此,如果你获得了一些东西以换取更慢的操作并且需要更多的内存和带宽,那么你只能使用double。确实有一些刚体物理学的应用,其中额外的精度是值得的,例如在操作lat \ lon时,单精度只能给你的仪表精度,但这是你的情况吗?

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