C++ (质量)相加/减法的精度误差

C++ (质量)相加/减法的精度误差,c++,precision,C++,Precision,如何以避免精度错误的方式缓存特定列表中所有浮点值的总和 例子 我有很多物理形状:m1,m2,m3,… 这些形状结合在一起形成一个巨大的物体,其质量M=m1+m2+m3+.. 我必须经常请求大物体的质量,所以我缓存M 现在,我有责任根据需要更新M 当我添加质量为mi的形状时:- M += mi; M -= mi; 当我移除质量为mi的形状时:- M += mi; M -= mi; 问题 程序添加/删除形状一段时间后, M距离正确的总和越来越远。(m1+m2+m3+..) 结果,我的程序最终

如何以避免精度错误的方式缓存特定列表中所有浮点值的总和

例子 我有很多物理形状:
m1
m2
m3
,…
这些形状结合在一起形成一个巨大的物体,其质量
M
=
m1
+
m2
+
m3
+..
我必须经常请求大物体的质量,所以我缓存
M

现在,我有责任根据需要更新
M

当我添加质量为mi的形状时:-

M += mi;
M -= mi;
当我移除质量为mi的形状时:-

M += mi;
M -= mi;
问题 程序添加/删除形状一段时间后,
M
距离正确的总和越来越远。(
m1
+
m2
+
m3
+..)

结果,我的程序最终执行异常。
毫无疑问,如果某对
mi
mj
的质量比非常低或非常高,症状会更快地表现出来

问题: 如何从专业角度缓解这一数字问题

换句话说:-
我是否应该一开始就不缓存求和,
M
我是否应该在每次添加/删除小形状后(以暴力方式)或(可能)在某些调用方请求
M
之前重新计算求和


我读过,它只能推迟问题。

问题是,如果指数不同,浮点结果是顺序相关的。例如,如果你

1e0 + 1e20 - 1e20
你会得到

0.0
因为
1e0+1e20==1e20
。但如果你这么做了

1e20 - 1e20 + 1e0
你会得到

1e0
所以一般来说,你应该把质量加起来,永远不要减法。并应首先将最低值相加,以便它们有可能影响最终结果。如果先求最大值的和,那么小值永远不会改变和

根据需要添加的数量,可以将体量缓存到组中,只对受影响的组重新求和,然后合并组的体量。我假设这里有很多实体,所以求和可能会很昂贵(例如,你要添加一百万个实体或类似的东西)


但是,如果您只是对一个小数字求和,那么可能不值得对其进行优化。您应该先编写代码,这样才能正常工作,然后对其进行分析以找到热点。如果你在做物理模拟,除法或平方根之类的东西要比加法昂贵得多。

基本问题是你假设浮点类型(
浮点型、
双精度型或你正在使用的任何类型)代表实数。它们不是——它们代表了一个离散近似。。。。
double
的精度通常为15-17位有效数字,而
float
的精度通常为7或8位有效数字

这意味着您存储的许多值将大致存储(即与您想要的值相比存在相关错误)。例如,
0.1
不能准确地存储在浮点中(因为它不能表示为
2
的负幂之和-实际上,这就是浮点类型中尾数的典型表示方式)

下一个影响是错误传播。任何加法、减法、乘法、除法、幂运算等操作数都有潜在的错误,这些错误会在结果中传播——可能放大,也可能衰减。处理这一问题的“专业”方法是命令操作以减少错误的传播(并预测产生的错误是什么,而不是假设精确的计算)


第三个影响是,加上或减去大小值会产生误差。因此
1.0
+
1.0e25
将给出
1.0e25
的结果。重复进行加法以获得结果,然后进行减法和再加法以保持值,这会传播这些类型的错误—操作顺序同样重要。因此
1.0+1.0e25-1.0e25
(假设从左到右进行操作)将给出(近似)零的结果,而
1.0e25-1.0e25+1.0
将给出(近似)1.0
的结果。这可能就是你们所看到的(因为物理计算中的质量可能很大,也可能很小)。解决方案不是以您目前的方式尝试优化结果,而是每次重新进行加法,或者以某种方式对质量(和其他相关计算)进行排序。这是一个值得接受的例子,它可以减少错误计算的机会。

< P>如果你知道质量的范围,你可以考虑使用不动点算法,并且使用 In64×t < /Cord>这将给你19.5位数的精度,而且只要你从不溢出,求和和和减法可以按任何顺序进行,而且总是精确的。

标准方法是对中间结果使用更高的精度。例如,如果输入为浮动
,则使用双精度。您将失去double的精度,但浮点值将保持不变。@GMichael-double可以使问题不那么严重,但您可以尝试使用
float
double添加1e0+1e20,但仍然会得到错误的答案(即,您将得到
1e20
,而不是1000000000000000001)@MarkLakata我看到了您的答案。你理论上是对的。但这与实践无关。没有人会把质子和太阳的质量相加。任何实验都是在相同的质量范围内进行的,因此,我的答案是相关的。问题不是把质子和太阳相加,而是把许多质子相加。如果结果是
float
,则只需将1e8个质子相加,就可以得出错误的总和。是的,你可以把它扩展到1e16质子,如果