【问题标题】:C#: Calculation using numbers larger than 3 billion is returning NaNC#:使用大于 30 亿的数字进行计算返回 NaN
【发布时间】:2020-06-18 14:19:33
【问题描述】:

总的来说,我对 C# 和编程非常陌生。我正在尝试计算一条线和一个椭圆的交点。 当椭圆的半径大于 1f 时,代码按预期工作。

当半径(大约)小于 1f 时,“t”返回 NaN。

我手动计算了这些数字,我注意到当半径下降到 1f 以下时,B * B = 30 亿+。

我尝试将“t”变成双精度,并且尝试使用 System.Math 而不是 Mathf。 当半径

非常感谢任何反馈!

public static Vector2 GetEllipseOuterIntersection(Vector2 ellipseCentre, float radius, Vector2 lightDot, Vector2 objectDot) {

    lightDot.x -= ellipseCentre.x;
    lightDot.y -= ellipseCentre.y;
    objectDot.x -= ellipseCentre.x;
    objectDot.y -= ellipseCentre.y;

    // Get the semiminor axis.
    float vertRadius = radius/2;

    // Calculate the quadratic parameters.
    float A = (objectDot.x - lightDot.x) * (objectDot.x - lightDot.x) / radius / radius + (objectDot.y - lightDot.y) * (objectDot.y - lightDot.y) / vertRadius / vertRadius;
    float B = 2 * lightDot.x * (objectDot.x - lightDot.x) / radius / radius + 2 * lightDot.y * (objectDot.y - lightDot.y) / vertRadius / vertRadius;
    float C = lightDot.x * lightDot.x / radius / radius + lightDot.y * lightDot.y / vertRadius / vertRadius - 1;

    double t = (-B + System.Math.Sqrt(B*B - (4*A*C))) / 2 / A;

    print(t);

    float x = (float) (lightDot.x + (objectDot.x - lightDot.x) * t + ellipseCentre.x);
    float y = (float) (lightDot.y + (objectDot.y - lightDot.y) * t + ellipseCentre.y);

    Vector2 outerIntersection = new Vector2(x, y);

    return outerIntersection;
}

【问题讨论】:

  • 你知道NaN 代表什么吗?如果不是,它表示一个不是数字的值。并尝试将NaN 分配给数字类型将导致错误。恕我直言,您可以考虑使用BigInteger
  • 你能设置一个条件断点并回溯为什么你会得到 NaN 吗? msdn 中提到的一种情况是0/0。这可以单独处理。

标签: c# unity3d math


【解决方案1】:

只要您的任何计算的任何组成部分是 NaN,那么整个结果也将是 NaN

NaN 与增长到大的数字无关(这会引发异常或表现出意外 - 例如翻转为负值)。而是暗示,在您的计算中,您正在创建一个数学上未定义的值。

当运算结果未定义时,方法或运算符返回 NaN。例如,零除以零的结果是 NaN,如下例所示。 (但请注意,将非零数除以零会返回 PositiveInfinity 或 NegativeInfinity,具体取决于除数的符号。)

除了3 Billion * 3 Billion 将是9E+24 之外,float 的最大值是3.40282347E+38 所以这无论如何都不是问题。

您可能在某处除以/ 0

可能发生这种情况的地方,例如发生在

  • 当你传入radius = 0

    因为在很多情况下你被/ radius除以

  • 当你传入radius = 2

    因为后来你在做

    vertRadius = radius / 2; 
    

    => 是 1 然后你做 / vertRadius - 1 例如为C

    所以vertRadius - 1 将是0

  • 如果A = 0

  • 如果objectDot.x - lightDot.x = 0

    因为那时ABC都是0

  • 如果B*B - 4*A*C = 0

    因为负数的平方根也是未定义的,而你这样做了

    System.Math.Sqrt(B*B - 4*A*C)
    

您应该拆分计算并添加检查以使调试、读取和维护更容易,例如有点像

public static Vector2 GetEllipseOuterIntersection(Vector2 ellipseCentre, float radius, Vector2 lightDot, Vector2 objectDot) 
{
    if(Mathf.Approximately(radius, 0))
    {
        Debug.LogError("radius may not be 0");
        return default;
    }

    if(Mathf.Approximately(radius, 2))
    {
        Debug.LogError("radius may not be 2");
        return default;
    }

    lightDot.x -= ellipseCentre.x;
    lightDot.y -= ellipseCentre.y;
    objectDot.x -= ellipseCentre.x;
    objectDot.y -= ellipseCentre.y;

    // Get the semiminor axis.
    float vertRadius = radius/2;

    var difX = objectDot.x - lightDot.x;
    var difY = objectDot.y - lightDot.y;

    if(Mathf.Approximately(difX, 0))
    {
        Debug.LogError("objectDot.x - lightDot.x may not be 0!");
        return default;
    }

    if(Mathf.Approximately(difY, 0))
    {
        Debug.LogError("objectDot.y - lightDot.y may not be 0!");
        return default;
    }

    var difXSqr = difX * difX;
    var difYSqr = difY * difY;
    var radiusSqr = radius * radius;
    var vertRadiusSqr = vertRadius * vertRadius;
    var lightDotXSqr = lightDot.x * lightDot.x;
    var lightDotYSqr = lightDot.y * lightDot.y;

    // Calculate the quadratic parameters.
    float A = difXSqr / radiusSqrInverse + difYSqr / vertRadiusSqr;
    float B = 2 * lightDot.x * difX / radiusSqr + 2 * lightDot.y * difY / vertRadiusSqr;
    float C = lightDotXSqr / radiusSqr + lightDotYSqr / vertRadiusSqr - 1;

    if(Mathf.Approximately(A, 0))
    {
        Debug.LogError("A may not be 0!");
        return default;
    }

    double BSquare = B*B;
    double AC4 = 4*A*C;
    double DIF = BSquare - AC4;

    if(DIF < 0)
    {
        Debug.LogError("DIF may not be negative!");
        return default;
    }

    double t = (-B + System.Math.Sqrt(DIF)) / 2 / A;

    print(t);

    float x = (float) (lightDot.x + difX * t + ellipseCentre.x);
    float y = (float) (lightDot.y + difY * t + ellipseCentre.y);

    return new Vector2(x, y);
}

【讨论】:

  • 感谢您的深刻回答!我进行了许多测试,问题出在平方根上。它变成了一个负数。这是由于我的椭圆变小了,超出了我的线条范围,由于多种原因,这对肉眼来说并不明显。我将寻求其他方法来解决我的特殊问题,非常感谢您的帮助!
【解决方案2】:

尝试使用 long。

长值比整数高得多。

// first number
long myLongNumber = 2345608830;

// second number
long secondLong = myLongNumber * 3;

secondLong 的结果:7036826490

编辑:我没有看到你需要小数。如果四舍五入是个问题,那么您可能需要其他解决方案。

【讨论】:

  • 我认为这不是问题......如果数字增长到很高,你会得到一个异常或只是奇怪的值(例如翻转到负值)。 NaN 在大多数情况下,而是暗示除以 0 或任何其他未定义的数字(例如 tangens(90°) 等)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-07-24
  • 1970-01-01
  • 1970-01-01
  • 2012-10-11
  • 1970-01-01
  • 1970-01-01
  • 2020-12-27
相关资源
最近更新 更多