Java 最小化累积浮点算术错误
我有一个3D空间中的2D凸多边形和一个测量多边形面积的函数Java 最小化累积浮点算术错误,java,math,vector,Java,Math,Vector,我有一个3D空间中的2D凸多边形和一个测量多边形面积的函数 public double area() { if (vertices.size() >= 3) { double area = 0; Vector3 origin = vertices.get(0); Vector3 prev = vertices.get(1).clone(); prev.sub(origin); for (int i =
public double area() {
if (vertices.size() >= 3) {
double area = 0;
Vector3 origin = vertices.get(0);
Vector3 prev = vertices.get(1).clone();
prev.sub(origin);
for (int i = 2; i < vertices.size(); i++) {
Vector3 current = vertices.get(i).clone();
current.sub(origin);
Vector3 cross = prev.cross(current);
area += cross.magnitude();
prev = current;
}
area /= 2;
return area;
} else {
return 0;
}
}
以下是我的程序的输出,格式为“迭代:区域”:
由于这最终将用于物理引擎,我想知道如何将累积误差降至最低,因为Vector3.rotate()方法将定期使用
谢谢
几个奇怪的注释:
- 误差与旋转量成正比。即:每次迭代的旋转次数越大->每次迭代的误差越大
- 将double传递给rotate函数时的错误比传递float函数时的错误更多
+= cross.magnitude
但是我不得不说,整个函数看起来有点奇怪。为什么它需要知道前面的顶点来计算当前面积?重复的浮点三角运算总是会有一些累积误差-这就是它们的工作原理。要处理它,基本上有两个选项:
假设近似值的有效值约为1,这是有意义的−3.417825×10-18,这在正常的双精度浮点误差范围1内。如果是这种情况,平方面积将继续以非常缓慢的指数衰减方式向零递减,因此需要大约20亿(2×109)7.3×1014次迭代,将面积减少到399。假设每秒迭代100次,大约7个半月,即23万年 编辑:当我第一次计算面积达到399需要多长时间时,似乎我犯了一个错误,不知何故高估了衰变率大约400000(!)。我已经纠正了上面的错误
使用浮点数的优点可能是由于某些截断/舍入。这可能暗示某种舍入方案会减少错误。此外,这些数字并不特别需要担心。谢谢,我认为你是对的,我将不得不忍受这些错误。我认为错误仍然是累积的,因为它是旋转的每次迭代都会产生一个越来越不准确的位置向量。面积函数从第一个顶点到顶点i和i+1取向量。然后取由这两个向量组成的平行四边形的面积(计算结果是叉积的大小)然后就是对每个i求和,然后在最后取一半,因为我们想要的是三角形的和,而不是平行四边形的和。存储之前的顶点只是稍微加快了速度:我想我看到了-实际上你看到的是导致混沌理论和术语“蝴蝶效应”的原因-一位计算机科学家用这个不精确的to显示变化非常小的复杂系统会很快变得混沌@null0pointer这很酷,我不知道混沌理论是从计算机科学中产生的:)@PaulSullivan-注意,误差本质上是一个随机游动,它会在多个维度上发散。而且,是的,误差是累积的。非常好而且有根据的答案——比我的“门外汉”理解要好得多。哇,感谢你花时间仔细观察这个区域的衰变,我甚至没有想到要花多长时间。一旦完全模拟发生,旋转调用的速度将永远不会达到每秒100次。此外,我已经将一个多面体存储为一组顶点的基本坐标,以及向量3中每个轴的方向。我没有建立逻辑上的联系,这将减轻任何积累。回答得很好。抱歉@PaulSullivan,我得把正确答案改成这个。
public void rotate(double xAngle, double yAngle, double zAngle) {
double oldY = y;
double oldZ = z;
y = oldY * Math.cos(xAngle) - oldZ * Math.sin(xAngle);
z = oldY * Math.sin(xAngle) + oldZ * Math.cos(xAngle);
oldZ = z;
double oldX = x;
z = oldZ * Math.cos(yAngle) - oldX * Math.sin(yAngle);
x = oldZ * Math.sin(yAngle) + oldX * Math.cos(yAngle);
oldX = x;
oldY = y;
x = oldX * Math.cos(zAngle) - oldY * Math.sin(zAngle);
y = oldX * Math.sin(zAngle) + oldY * Math.cos(zAngle);
}
0: 400.0
1000: 399.9999999999981
2000: 399.99999999999744
3000: 399.9999999999959
4000: 399.9999999999924
5000: 399.9999999999912
6000: 399.99999999999187
7000: 399.9999999999892
8000: 399.9999999999868
9000: 399.99999999998664
10000: 399.99999999998386
11000: 399.99999999998283
12000: 399.99999999998215
13000: 399.9999999999805
14000: 399.99999999998016
15000: 399.99999999997897
16000: 399.9999999999782
17000: 399.99999999997715
18000: 399.99999999997726
19000: 399.9999999999769
20000: 399.99999999997584
+= cross.magnitude