C#: расчет с использованием чисел больше 3 миллиардов возвращает NaN - PullRequest
0 голосов
/ 18 июня 2020

Я новичок в C# и программировании в целом. Я пытаюсь вычислить пересечение линии и эллипса. Код работает должным образом, если радиус эллипса больше 1f.

Когда радиус (приблизительно) меньше 1f, «t» возвращает NaN.

Я вычислил числа вручную, и я заметил, что когда радиус опускается ниже 1f, B * B = 3 миллиарда +.

Я попытался превратить «t» в двойное, и я попытался использовать System.Math вместо Mathf. "t" по-прежнему возвращает NaN, если радиус <1f. </p>

Любая обратная связь приветствуется!

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;
}

Ответы [ 2 ]

0 голосов
/ 18 июня 2020

Если какой-либо компонент любого из ваших вычислений равен 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

    потому что тогда A, B и C - это 0

  • , если B*B - 4*A*C = 0

    , потому что также квадрат- root отрицательных чисел не определен, и вы делаете

    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);
}
0 голосов
/ 18 июня 2020

Попробуйте вместо этого использовать long.

Значения Long go намного выше, чем целые числа.

// first number
long myLongNumber = 2345608830;

// second number
long secondLong = myLongNumber * 3;

результат secondLong: 7036826490

Изменить: я не не вижу, что вам нужны десятичные дроби. Если округление до целого числа является проблемой, вам может потребоваться другое решение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...