浮点精度和物理计算
我的物理世界中的重力 Vector2 是 (0; 0.1)。
众所周知,数字 0.1 是有问题的,因为“它无法精确表示,但大约为 1.10011001100110011001101 × 2-4”。
具有这个重力值会给我带来碰撞问题并产生非常讨厌的错误。将值更改为 0.11 可解决这些问题。
是否有更优雅的解决方案,根本不需要更改值?
该错误的视频
http://www.youtube.com/watch?v=bRynch1EtnE
源码
第一种方法(AABBIOverlapping) 检查两个 AABB 实体之间的交集。 每帧为每个主体调用第二个方法(Update)。
我将尝试解释 Update 方法的工作原理:
- 将重力加速度矢量添加到速度矢量
- 创建一个临时矢量(下一步)并将其设置为速度
- 在当前主体周围的单元格中的空间散列中获取主体
- 如果有水平重叠,解决它,将next.X和velocity.X设置为0并移动玩家
- 如果存在垂直重叠,解决它,将next.Y和velocity.Y设置为0(或设置为0.1以防止不断从 移动播放器
- 天花板)并在循环后 ,如果没有重叠,则移动播放器
The gravity Vector2 in my physics world is (0; 0.1).
The number 0.1 is known to be problematic, since "it cannot be represented exactly, but is approximately 1.10011001100110011001101 × 2-4".
Having this value for the gravity gives me problems with collisions and creates quite nasty bugs. Changing the value to 0.11 solves these problems.
Is there a more elegant solution that doesn't require changing the value at all?
Video of the bug
http://www.youtube.com/watch?v=bRynch1EtnE
Source code
The first method (AABBIsOverlapping) checks for intersection betweens two AABB entities.
The second method (Update) is called for each body every frame.
I'll try to explain how the Update method works:
- Add the gravity acceleration vector to the velocity vector
- Create a temp vector (next) and set it to the velocity
- Get bodies in the spatial hash in the cells around the current body
- If there is an horizontal overlap, resolve it, set next.X and velocity.X to 0 and move the player
- If there is a vertical overlap, resolve it, set next.Y and velocity.Y to 0 (or to 0.1 to prevent constant jumping from ceilings) and move the player
- After the loop, if there were no overlaps, move the player
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
简而言之,没有。通常的解决方案是从不检查相等性,并始终检查范围 +- epsilon(非常小的值)。
In short no. The usual solution is to never check for equality, and always check for a range +- epsilon (very small value).
在物理学中,无法表示数字根本不重要。物理学中唯一重要的是舍入误差的累积。
我认为您的问题与不正确的 epsilon 比较有关,甚至与没有 epsilon 的比较有关。但如果没有更多信息,我无法帮助你。如果您的代码无法处理小的舍入误差,则它是有缺陷的,需要修复。
您可以使用
Decimal
作为数学代码,它可以准确地表示0.1m
。但我认为这并不是您真正需要的,因为您的问题很可能与0.1
无法在float
中精确表示这一事实无关。我在代码中看到的一个潜在问题是,在解决碰撞时,您将主体准确移出到碰撞边界。也许您需要将其进一步移出 epsilon。
In physics being unable to represent a number shouldn't matter at all. The only thing that matters in physics is the accumulation of rounding errors.
I assume your problem is related to incorrect epsilon comparisons, or even comparisons without epsilon. But without more information I can't help you. If your code can't deal with small rounding errors it is flawed and needs to be fixed.
You could use
Decimal
for your math code, which can represent0.1m
exactly. But I don't think that's what you really need since your problem is most likely unrelated to the fact that0.1
can't be represented exactly infloat
.One potential problem I see in your code is that when resolving collisions you move out the body exactly to the collision border. Perhaps you need to move it out an epsilon further.
如果不可表示性导致代码中出现错误,那么您的算法在某种程度上存在缺陷。很难说它们有什么缺陷,因为你没有分享任何细节,但使用可表示的数字并不能解决这个问题。
事实上 0.11 也无法表示,所以如果它解决了你的问题,那么它是出于除此之外的其他原因。
If non-representability is resulting in bugs in your code then your algorithms are flawed in some way. It's hard to say how they are flawed because you have not shared any details, but using a representable number won't fix it.
In fact 0.11 is not representable either and so if it solves your problem, it does so for some reason other than this.
简而言之 - 您无法像在现实生活中那样在 PC 上使用浮点值。由于用于存储非常宽范围内的值的内存量有限,因此总是会出现精度损失和舍入错误。
始终检查与某些 epsilon 的相等性,该 epsilon 可能是工作值之间步长的一半,例如 0.1:
或给定比例和给定浮点格式的最佳精度(例如,64 位的 epsilon 明显小于 32 位)(您可能需要检查您的获取该值的方法的语言):
编辑:
您没有提到您遇到的错误。那么 0.1 到底有什么问题呢?
我只能假设你的时间步长不够精确,你的物体速度允许它们在碰撞检查之间相互穿过。这是正确的吗?如果是 - 您应该增加时间步分辨率和/或尽早检查碰撞。
In short - You can not work with floating-point values on PC as in real life. There's always gonna be precision loss and rounding errors due to limited amount of memory used to store the values within very wide ranges.
Always check for equality with some epsilon which could be half the step between working values, e.g. 0.1:
or best precision at given scale and given floating-point format (e.g. 64bit has smaller epsilon than 32bit obviously) (you may need to check with your language for ways to obtaining that value):
EDIT:
You said nothing about bugs you are having. So what is exactly wrong with 0.1?
I could only assume that your timestep is not precise enough, your objects speeds allow them to pass through each other inbetween collision checks. Is that correct? If yes - you should increase timestep resolution and/or check for collisions earlier.