有这个代码:
Debug.Log((p > 10));
变量 p 还包含数字 10。
我正在运行给定代码的 100 次迭代。
结果是:90 条虚假消息和 10 条真实消息
所有消息都必须为 False。
为了防止这种情况发生,Mathf.Approximately 用于 == 运算符。> 和 < 运算符呢?我试过这样,但没有帮助:
Debug.Log((p > 10 + Mathf.Epsilon));
如何解决问题?
有这个代码:
Debug.Log((p > 10));
变量 p 还包含数字 10。
我正在运行给定代码的 100 次迭代。
结果是:90 条虚假消息和 10 条真实消息
所有消息都必须为 False。
为了防止这种情况发生,Mathf.Approximately 用于 == 运算符。> 和 < 运算符呢?我试过这样,但没有帮助:
Debug.Log((p > 10 + Mathf.Epsilon));
如何解决问题?
花了很长时间,但我希望它很有趣。然后我已经厌倦了用两行写答案:)
老故事
我在大学读书的时候,遇到了一个很大的计算问题,有两种计算方式。最后,这两个答案必须收敛。老师允许有20%的差异。但问题是一个计算分支大约只有半百个乘法。还有三角函数和对数。如果这个人明白他在做什么,那么计算本身大约需要一个小时。
并且计算分析没有显示错误。如果邻居做了同样的计算,那么他得到了他自己的一对数字。当我有 3 对数字时,我意识到这行不通。
数学家睁开眼睛
但后来原因变成了——在计算器上进行计算时,我几乎总是自动四舍五入到小数点后 2-3 位(懒得重写 10-12 位)。数学老师看着这个说,很容易得到一倍半到两倍的点差(其实是观察到的)。
因此,我用 Delfi 武装自己并编写了自己的计算。最后,它以百分之一的准确度结合在一起。对比我的手工计算,发现误差逐渐累积。但是接受作业的老师有点震惊(她没有看到数字会收敛这么多,并试图用计算器找到我“适合”的确切位置)。
物理学家也知道
因此,当计算的结果写在物理学中时(例如,在实验室工作或博士论文中),没有指明错误,它们“没有任何物理意义”。
然后我用每一步的误差计算做了另一个计算,一切都在一起了,但更多的是下面。
现在更接近这个问题了。
让我们看看获得数字 10 的这些选项(是的,这段代码是有利的,但它不会改变任何东西)
结论
尽管所有这些方法都给出了 10,但它们都是有点“不同的 10”。在第二和第五种情况下,编译器优化并且有“诚实的10.0”(是的,它们是编译器。有时它们可以创造奇迹并隐藏用户错误。
现在您需要了解两个实数的 == 运算符仅进行按位比较(如果类型不匹配,则首先需要“调整”它们,这些是附加错误)。
还有,马上就清楚,在第四种情况下,输出的时候,看起来好像有10个,其实不然。
怎么会这样?
现在让我们走得更近。在您的代码中,您似乎正在执行一些应该给出相同结果的计算。但是你得到它们有点不同(例如,这些可以是使用蒙特卡洛方法计算图形的面积)。
游戏也是最新的
游戏中也有类似的情况。假设我们有一个步行者,而主角只能以一定大小的步数移动。自然地,我们将步长添加/减去其当前坐标。而在房间里走来走去,回到起点,我们可以发现,我们已经发生了一点变化。这有时是“非常出乎意料的”。在同一个射击游戏中,一颗子弹可以在“附近”飞行,尽管我们的目标非常准确。
我们正在寻找解决方案
该怎么办?有必要考虑结果中的错误。有时他们建议使用同一个
EPS,但这不是正确的方法。我们需要比较两个结果,而不是作为点结果,而是作为两个段。简单复杂的例子
让我们看一下这样的比较 10±3 和 9±2。如果我们将它们翻译成“段”,它将是 [7;13] 和 [7;11]。最有可能的是,大多数人会考虑第一个而不是第二个。但是,考虑到误差,它们可以相等。他们可能不是。因此,正确答案是:10±3 ≊ 9±2(这等于或几乎等于)
新版本 10±3 和 5±2 -> [7;13] 和 [3;7]。尽管乍一看似乎第二个数字肯定更少,但事实并非如此。在某些情况下,它们可以等于 7。是的,这种可能性非常低,但仍然存在。因此,这里正确的符号是 10±3 ≥ 5±2,不管它多么奇怪。
但是很多人都忘记了。然后得到了奇怪的结果……然后突然,“错误有错误吗?”
怎样成为?
考虑到这一切,如何比较?在这里,您总是需要查看目标任务。例如,如果这是地图上一个人的坐标,那么最好将坐标四舍五入到步重数,甚至将它们存储为整数。如果这些是船舶坐标,那么将船舶尺寸加倍将是准确度的一个很好的衡量标准。如果这是原子弹的计算,那么看起来他们在论坛上犯了一个错误。
做出决定
让我们继续看代码。我会做这个比较更多
什么可以改写以及如何改写
其中 eps 要么通过“戳”从合适的参考书中选择,要么只是两个变量的测量误差之和。
这已经是对这个问题的正式回答
让我们看看你的比较方式有什么问题
他看起来像我的。但是如果你看看Mathf.Epsilon是什么,就会发现
假设它是一个“原子”(即不可分割的,最小的)正数。不再可能将其分成两半。稍微想一想,很明显和他的比较有点没有意义——这个数字太小了
无论如何,上面的链接都说了同样的话。
(这是某种机器翻译,但似乎可以理解)。
附言
我会把这个玩具放在这里https://evanw.github.io/float-toy/ - 它有助于理解里面的一切是如何旋转的。