浮点精度和物理计算

时间:2011-06-21 08:19:41

标签: c# floating-point physics

物理世界中的引力Vector2是(0; 0.1)。

已知数字0.1有问题,因为“它无法准确表示,但约为1.10011001100110011001101×2-4”。

拥有重力的这个值让我遇到了碰撞的问题,并产生了非常讨厌的错误。将值更改为0.11可以解决这些问题。

是否有一个更优雅的解决方案,根本不需要更改值?


错误视频

http://www.youtube.com/watch?v=bRynch1EtnE


源代码

http://pastebin.com/jNkqa3sg

第一种方法(AABBIsOverlapping)检查两个AABB实体之间的交集。 每帧为每个主体调用第二个方法(Update)。

我将尝试解释Update方法的工作原理:

  1. 将重力加速度矢量添加到速度矢量
  2. 创建临时矢量(下一个)并将其设置为速度
  3. 在当前正文周围的单元格中的空间哈希中获取实体
  4. 如果存在水平重叠,请解决它,将next.X和velocity.X设置为0并移动播放器
  5. 如果存在垂直重叠,请解决它,将next.Y和velocity.Y设置为0(或设置为0.1以防止从天花板不断跳跃)并移动播放器
  6. 循环结束后,如果没有重叠,请移动播放器

4 个答案:

答案 0 :(得分:6)

总之没有。通常的解决方案是永远不检查相等性,并始终检查范围+ - epsilon(非常小的值)。

答案 1 :(得分:4)

物理学中无法代表数字根本不重要。在物理学中唯一重要的是舍入误差的累积。

我认为你的问题与错误的epsilon比较有关,甚至与没有epsilon的比较有关。但没有更多的信息,我无法帮助你。如果您的代码无法处理小的舍入错误,则它存在缺陷并需要修复。

您可以使用Decimal作为数学代码,它可以完全代表0.1m。但我不认为这是你真正需要的,因为你的问题很可能与0.1无法在float完全无法表示的事实无关。


我在代码中看到的一个潜在问题是,在解决碰撞时,您将身体完全移出到碰撞边界。也许你需要进一步将它移出epsilon。

答案 2 :(得分:2)

如果不可表示性导致代码中出现错误,那么您的算法在某些方面存在缺陷。很难说它们是如何存在缺陷的,因为你没有共享任何细节,但使用可表示的数字将无法修复它。

事实上0.11也不具有代表性,所以如果它能解决你的问题,那么除了这个之外,它还是出于某种原因。

答案 3 :(得分:2)

简而言之 - 您不能像在现实生活中一样使用PC上的浮点值。由于用于在非常宽的范围内存储值的内存量有限,总会出现精度损失和舍入误差。

始终检查与某些epsilon的相等性,这可能是工作值之间的一半,例如0.1:

IsEqual(0.1, 0.2, 0.05) = false
IsEqual(0.1, 0.1001, 0.05) = true
IsEqual(0.1, 0.1499, 0.05) = true

或给定比例下的最佳精度和给定的浮点格式(例如,64位的epsilon明显小于32位)(您可能需要检查您的语言以获取该值的方法):

IsEqual(0.1, 0.2, BestPrecisionAt(0.1)) = false
IsEqual(0.1, 0.1001, BestPrecisionAt(0.1)) = false
IsEqual(0.1, 0.1499, BestPrecisionAt(0.1)) = false
IsEqual(0.1, 0.1000001, BestPrecisionAt(0.1)) = true
//Where for example BestPrecisionAt(0.1) could be 0.00001

修改 你没有说过你遇到的错误。那么0.1的错误是什么? 我只能假设你的时间步长不够精确,你的物体速度允许它们在碰撞检查之间相互通过。那是对的吗?如果是 - 您应该提高时间步长分辨率和/或更早地检查碰撞。