Проблемы с NaN - PullRequest
       3

Проблемы с NaN

0 голосов
/ 15 марта 2012

У меня были проблемы с моим кодом в течение двух недель, и я не смог его отладить. Я пришел сюда в надежде, что кто-то может помочь. Я написал программу, которая использует алгоритм Барнса-Хата для гравитационного моделирования n-тела. Моя проблема в том, что одна или несколько «частиц» будут иметь назначенную им позицию {NaN, NaN, NaN} (используя три двойных, чтобы представить x, y, z трехмерного пространства). Это, в свою очередь, заставляет другие частицы иметь ускорение {NaN, NaN, NaN} и, в свою очередь, скорость и положение {NaN, NaN, NaN}. По сути, после одного или двух кадров все исчезает. Кажется, это происходит в методе updateAcc, но у меня есть ощущение, что это не так. Я понимаю, что это огромное начинание, и я очень благодарен всем, кто мне помогает. Что я проверил: Нет отрицательных квадратных корней, и все значения, кажется, находятся в их пределах. Исходный код доступен здесь . Еще раз спасибо.

Код, который, кажется, производит NaN:

private static void getAcc(particle particle, node node)
{
    if ((node.particle == null && node.children == null) || node.particle == particle)
    {
        //Geting gravity to a node that is either empty or the same node...
    }
    else if (distance(node.centerOfMass, particle.position) / node.sideLength > theta && node.children != null)
    {
        for (int i = 0; i < node.children.length; i++)
        {
            if (node.children[i] != null)
            {
                getAcc(particle, node.children[i]);
            }
        }
    }
    else
    {
        particle.acceleration = vecAdd(particle.acceleration, vecDiv(getForce(particle.position, particle.mass, node.centerOfMass, node.containedMass), particle.mass));
    }
}
private static double sumDeltaSquare(double[] pos1, double[] pos2)
{
    return Math.pow(pos1[0]-pos2[0],2)+Math.pow(pos1[1]-pos2[1],2)+Math.pow(pos1[2]-pos2[2],2);
}
private static double[] getForce(double[] pos1, double m1, double[] pos2, double m2)
{
    double ratio = G*m1*m2;
    ratio /= sumDeltaSquare(pos1, pos2);
    ratio /= Math.sqrt(sumDeltaSquare(pos1,pos2));
    return vecMul(vecSub(pos2, pos1), ratio);
}
private static double distance(double[] position, double[] center)
{
    double distance = Math.sqrt(Math.pow(position[0]-center[0],2) + Math.pow(position[1]-center[1],2) + Math.pow(position[2]-center[2],2));
    return distance;
}

Ответы [ 3 ]

1 голос
/ 15 марта 2012

Я не уверен, что это единственная проблема, но это начало.

sumDeltaSquare иногда возвращает 0, что означает, что когда значение используется в getForce ratio /= sumDeltaSquare(pos1, pos2);, оно будет создавать бесконечность и вызывать проблемы.

Это серьезная проблема, которую нужно отладить и решить, что все значит. Мне нравилось смотреть на точки.

1 голос
/ 17 марта 2012

Во-первых, почему вы не используете библиотеку Java Vecmath ? (она распространяется как часть Java3D. Загрузите двоичную сборку Java3D, а затем просто используйте vecmath.jar) Ваша проблемаочень вероятно, где-то в ваших пользовательских векторных функций.Если нет, то @pimaster, вероятно, прав в том, что ваш метод величины перевода sumDeltaSquare может возвращать 0, если две ваши массы занимают один пробел.Это означает, что если вы не находитесь внутри черной дыры, вы делаете это неправильно: P.Или вам нужно придумать теорию квантовой гравитации, прежде чем вы сможете выполнить это моделирование.

Если вы не можете использовать vecmath (т.е. это домашнее задание), я бы предложил вам использовать регулярное выражение , чтобы найти каждый экземпляр return *, а затем заменить его на assert !Double.isNan(*) && Double.isFinite(*);\nreturn *, за исключением замены * для любого регулярного выражения, которое находит "группу совпадений".Я точно забыл, что это такое, но я начал с Google.Я также предлагаю вам избегать оптимизаций до тех пор, пока после у вас не будет рабочего кода.

0 голосов
/ 15 марта 2012

Я не собираюсь отлаживать ваш код. Но значения NaN являются результатом математически недопустимых операций над числами с плавающей запятой. Наиболее известным из них является деление на 0 (не генерирует исключение с плавающей запятой). Как это может произойти?

Если ваши вычисления дают очень маленькие числа, они могут стать слишком маленькими, чтобы быть представленными как 64-битные числа с плавающей запятой (для них требуется больше битов, чем доступно), и вместо этого Java вернет 0.0. В другом направлении, если вы получаете переполнение (величина числа требует слишком много битов), Java превращает это в бесконечность. Выполнение математики с бесконечностью и 0 может быстро привести к NaN, и NaN распространяется через каждую операцию, которую вы применяете к нему.

Подробнее см. В разделах 4.2 и 15.17 спецификации языка Java.

...