我的C代码有问题。 它是2D中N体问题的强制解决方案。 有时我将NaN作为struct instance params值。
我的猜测是分裂出了问题。我已经分析了很多案例,但仍然找不到导致值为NaN的出现模式。
这是我的代码:
#include <stdio.h>
#include <math.h>
#include <time.h>
#include <stdlib.h>
double G;
int N;
int T;
int COUNT;
typedef struct
{
double rx, ry;
double vx, vy;
double fx, fy;
double mass;
} Body;
void updateBody (Body* bodyInstance, int timestamp) {
bodyInstance->vx += timestamp * bodyInstance->fx / bodyInstance->mass;
bodyInstance->vy += timestamp * bodyInstance->fy / bodyInstance->mass;
bodyInstance->rx += timestamp * bodyInstance->vx;
bodyInstance->ry += timestamp * bodyInstance->vy;
};
void updateBodyForces (Body* bodyA, Body* bodyB) {
double dx, dy, dist, force;
dx = bodyB->rx - bodyA->rx;
dy = bodyB->rx - bodyA->rx;
// collision/same place in spacetime hack
if (bodyB->rx == bodyA->rx && bodyB->ry == bodyA->ry) {
dist = 1;
} else {
dist = sqrt(pow(dx, 2) + pow(dy, 2));
}
force = (G * bodyA->mass * bodyB->mass) / (pow(dist, 2) + 100);
bodyA->fx += force * dx / dist;
bodyA->fy += force * dy / dist;
}
void resetBodyForces (Body* bodyInstance) {
bodyInstance->fx = 0;
bodyInstance->fy = 0;
}
void getRandomBody (Body* bI) {
bI->rx = rand() % 10;
bI->ry = rand() % 10;
bI->vx = rand() % 10;
bI->vy = rand() % 10;
bI->fx = 0;
bI->fy = 0;
bI->mass = 20;
}
int main( int argc, char *argv[] ) {
G = argc >= 2 ? atof(argv[1]) : 0.01;
N = argc >= 3 ? atoi(argv[2]) : 3;
T = argc >= 4 ? atoi(argv[3]) : 1;
COUNT = argc >= 5 ? atoi(argv[4]) : 10;
srand(time(NULL));
Body bodies[N];
for (int i=0; i<N; i++) {
getRandomBody(&bodies[i]);
}
for (int i = 0; i < COUNT; i++) {
for (int j = 0; j < N; j++) {
resetBodyForces(&bodies[j]);
for (int k = 0; k < N; k++) {
if (j != k) {
updateBodyForces(&bodies[j], &bodies[k]);
}
}
}
for (int j = 0; j < N; j++) {
updateBody(&bodies[j], T);
}
}
}
答案 0 :(得分:4)
在updateBodyForces
中,您测试两个浮点值是否相等。它们可能与最后一个位差不多,约为1 / 10,000,000。
在此之后你取平均差的平方根,结果可能是0
(真的很零,0.0000000...
),这不是问题,但然后你除以这个数字。这是NaN的来源。
替换此部分
// collision/same place in spacetime hack
if (bodyB->rx == bodyA->rx && bodyB->ry == bodyA->ry) {
dist = 1;
}
基于FLT_EPSILON
的更明确的测试。有关详细说明,请参阅Floating point equality and tolerances。
经过一些测试:ε值难以猜测。由于您可以使用dist = 1
来处理极端情况,因此请在force
行上方的测试下方添加此内容,以确保:
if (dist < 1)
dist = 1;
所以你肯定不会得到任何NaN。这导致了这个更简单的功能:
void updateBodyForces (Body* bodyA, Body* bodyB) {
double dx, dy, dist, force;
dx = bodyB->rx - bodyA->rx;
dy = bodyB->ry - bodyA->ry;
dist = sqrt(dx*dx + dy*dy);
// collision/same place in spacetime hack
if (dist < 1)
dist = 1;
force = (G * bodyA->mass * bodyB->mass) / (pow(dist, 2) + 100);
bodyA->fx += force * dx / dist;
bodyA->fy += force * dy / dist;
}
通过将1
替换为较小的值,您可以使不稳定的时空攻击变得不那么明显。
答案 1 :(得分:3)
由于(i) base ,-NaN
结果的一个常见(以及迄今为止最可能的解释)生成sqrt
结果是pow
的否定参数bodyInstance->vx +=
的参数为负数,或者(ii)浮点变量中的笑话数字累加:sqrt
&amp; c。将积累舍入误差。
检查案例之前以致电NaN
。
您还会得到一个0.0 / 0.0
,其表达式为NaN
,但我从未见过平台在该实例中产生负dx * dx
。
所以这里的道德是偏好pow(dx, 2)
到dx
:前者更准确,不容易受到负hypot
的意外结果的影响,当然也不会慢。更好的是,使用C标准库中的proguard-rules.pro
。