Math.Abs​​()不会创建精确的绝对值

时间:2013-06-14 18:20:12

标签: c#

我正在研究一个模拟太空中物理/重力的项目,其中一部分是预测轨道和显示某些信息,其中一个是近地点和近视(轨道的最近和最远点)。实现这一目标的最简单方法是找到轨道中物体相对于静止物体的接近速度为0的点,但由于每秒的计算量有限,因此几乎不会发生该值精确为0的情况 - 所以我的我想的是比较每个循环,如果当前值的符号和之前的循环值已经改变了。

代码看起来类似于:

ArrayList orbit = new ArrayList();    
orbit.Add(newValue);

//In case the sign changes - to +

if(
   orbit.Count >= 2 &&
   physics.getVelocity(orbit[orbit.Count-1], otherObject) == Math.Abs(physics.getVelocity(orbit[orbit.Count-1], otherObject) &&
   physics.getVelocity(orbit[orbit.Count-2], otherObject) != Math.Abs(physics.getVelocity(orbit[orbit.Count-2], otherObject)
)

//In case the sign changes + to -

if(
   orbit.Count >= 2 &&
   physics.getVelocity(orbit[orbit.Count-1], otherObject) != Math.Abs(physics.getVelocity(orbit[orbit.Count-1], otherObject) &&
   physics.getVelocity(orbit[orbit.Count-2], otherObject) == Math.Abs(physics.getVelocity(orbit[orbit.Count-2], otherObject)
)

但有时它似乎没有用,所以我对这个变量进行了调整,并且找不到任何错误,总是有这样的模式:

Value (1): -0.4523452
Value (2): 1.2435235

Value (1): 0.4523452
Value (2): -1.2435235

但该算法忽略了一些事件,但仅在数字接近于0时(如上所述)我的观察结果,但如果它看起来像这样:

Value (1): 64.904534
Value (2): -7.3456800

很好,我无法弄清楚原因。我希望有人可以帮助我 - 如果还有任何问题,请询问:)

3 个答案:

答案 0 :(得分:5)

这种类型的检测,用于检测何时一个值为负值而另一个值为正(顺序无关紧要),您应该使用Math.Sign方法:

if (Math.Sign(a) != Math.Sign(b)) { ... }

然而,@ supercat的答案对于你当前的代码是正确的,浮点值就是那么挑剔。

答案 1 :(得分:4)

当使用浮点数时,通常应该假设每个计算都可以添加一些小但任意的软糖因子,即使在看起来两个计算序列应该相同地执行并且累积的情况下也是如此。同样的软糖因素,他们不会总是这样做。完全有可能的是,如果测试Math.Abs(x+y+z) == x+y+z,则Math.Abs的参数的计算将与等式运算符符号右侧的计算略有不同。

如果要测试数字是否小于零,请不要将其与绝对值进行比较。检查它是否小于零。将计算结果存储在变量中,将该变量传递给Math.Abs,并且比较相等性可能比在相等运算符的两边重复计算更可靠(无论计算得到的任何软糖因素应该得到存储在那个变量中),但为什么要这么麻烦?

答案 2 :(得分:0)

您可以尝试仅测试该值是否在特定值0内。

epsilon成为宽容。小于该特定数字距离的值将被视为等于该数字。

然后你可以写一个IsApproximately()谓词:

public static bool IsApproximately(double number1, double number2, double epsilon)
{
    return Math.Abs(number1 - number2) <= epsilon;
}

当你想要比较数字时,它会使它更具可读性,例如:

if (IsApproximately(velocity, 0, 0.0001))
{
    // Treat as zero
}