解决浮点舍入问题C ++

我开发了一种科学应用(模拟在细胞核中移动的染色体)。将染色体分成小片段,使用4x4旋转矩阵围绕随机轴旋转。 问题是模拟执行数千亿次旋转,因此浮点舍入误差会呈指数级增长并逐渐增长,因此随着时间的推移,碎片会“漂浮”并与染色体的其余部分分离。 我在C ++中使用双精度。软件暂时在CPU上运行,但将移植到CUDA,模拟最多可持续1个月。 我不知道我怎么能以某种方式重新规范化染色体,因为所有的片段都被链接在一起(你可以把它看成是一个双重链接列表),但我认为如果可能的话,这将是最好的想法。 你有什么建议吗 ?我觉得有点迷茫。 非常感谢你, H。 编辑: 添加了简化的示例代码。 您可以假设所有矩阵数学都是经典实现。
// Rotate 1000000 times
for (int i = 0; i < 1000000; ++i)
{
    // Pick a random section start
    int istart = rand() % chromosome->length;

    // Pick the end 20 segments further (cyclic)
    int iend = (istart + 20) % chromosome->length;

    // Build rotation axis
    Vector4 axis = chromosome->segments[istart].position - chromosome->segments[iend].position;
    axis.normalize();

    // Build rotation matrix and translation vector
    Matrix4 rotm(axis, rand() / float(RAND_MAX));
    Vector4 oldpos = chromosome->segments[istart].position;

    // Rotate each segment between istart and iend using rotm
    for (int j = (istart + 1) % chromosome->length; j != iend; ++j, j %= chromosome->length)
    {
        chromosome->segments[j].position -= oldpos;
        chromosome->segments[j].position.transform(rotm);
        chromosome->segments[j].position += oldpos;
    }
}
    
已邀请:
您需要为系统找到一些约束,并努力将其保持在一定的合理范围内。我做了一堆分子碰撞模拟,在那些系统中总能量是守恒的,所以每一步我都要仔细检查系统的总能量,如果它变化了一定的阈值,那么我知道我的时间步骤选择不当(太大或太小)我选择一个新的时间步骤并重新运行它。这样我就可以实时跟踪系统发生的情况。 对于这个模拟,我不知道你有多少守恒量,但如果你有一个,你可以尝试保持这个不变。请记住,缩短时间步长并不总能提高精度,您需要使用精度来优化步长。我已经进行了几周CPU时间的数值模拟,并且保守量总是在10 ^ 8中的1个部分,所以有可能,你只需要玩一些。 此外,正如Tomalak所说,也许会尝试始终将您的系统引用到开始时间而不是上一步。因此,不要总是移动你的染色体,将染色体保持在它们的起始位置,并与它们一起存储一个转换矩阵,将你带到当前位置。计算新旋转时,只需修改变换矩阵即可。这可能看起来很愚蠢,但有时这很有效,因为误差平均为0。 例如,假设我有一个位于(x,y)的粒子和我计算的每一步(dx,dy)并移动粒子。逐步的方式会做到这一点
t0 (x0,y0)
t1 (x0,y0) + (dx,dy) -> (x1, y1)
t2 (x1,y1) + (dx,dy) -> (x2, y2)
t3 (x2,y2) + (dx,dy) -> (x3, y3)
t4 (x3,30) + (dx,dy) -> (x4, y4)
...
如果你总是引用t0,你可以这样做
t0 (x0, y0) (0, 0)
t1 (x0, y0) (0, 0) + (dx, dy) -> (x0, y0) (dx1, dy1)
t2 (x0, y0) (dx1, dy1) + (dx, dy) -> (x0, y0) (dx2, dy2)
t3 (x0, y0) (dx2, dy2) + (dx, dy) -> (x0, y0) (dx3, dy3)
所以在任何时候,tn,要获得你的真实位置,你必须做(x0,y0)+(dxn,dyn) 现在像我的例子一样简单的翻译,你可能不会赢得很多。但对于轮换,这可以节省生命。只需保留一个与每个染色体相关的欧拉角矩阵,然后更新它而不是染色体的实际位置。至少这样他们就不会飘走了。     
编写公式,使时间步长
T
的数据不仅仅来自时间步长
T-1
中的浮点数据。尽量确保将浮点错误的产生限制在单个时间步长。 没有更具体的问题需要解决,这里很难说更具体的内容。     
问题描述相当模糊,所以这里有一些相当含糊的建议。 选项1: 找到一些约束条件,以便(1)它们应该始终保持,(2)如果它们失败,但仅仅是,它很容易调整系统以便它们执行,(3)如果它们全部保持,则模拟不是'变得非常疯狂,(4)当系统开始变得疯狂时,约束开始失败但只是轻微。例如,对于某些d,染色体的相邻位之间的距离可能最多为d,如果一些距离稍微大于d,那么您可以(例如)从一端沿着染色体走,修复通过将下一个片段与其前任以及所有后继片段一起移动而使任何距离过大。或者其他的东西。 然后经常检查约束,以确保捕获时任何违规仍然很小;当你发现违规行为时,请解决问题。 (你可能应该安排,当你解决问题时,你“不仅仅满足”约束。) 如果一直检查约束是否便宜,那么你当然可以做到这一点。 (这样做也可能使您能够更便宜地进行修复,例如,如果这意味着任何违规行为总是很小。) 选项2: 找到一种描述系统状态的新方法,使问题不可能出现。例如,也许(我怀疑这一点)你可以为每个相邻的片段对存储一个旋转矩阵,并强制它总是一个正交矩阵,然后让片段的位置由那些旋转矩阵隐式确定。 选项3: 而不是将约束视为约束,而是提供一些小的“恢复力”,这样当某些事情变得不合时,它往往会被拉回原来的状态。请注意,当没有任何问题时,恢复力为零或至少可以忽略不计,这样它们就不会比原始数值误差更严重地扰乱您的结果。     
我认为这取决于您使用的编译器。 Visual Studio编译器支持/ fp开关,它告诉浮点运算的行为 你可以阅读更多相关信息。基本上,/ fp:strict是最严厉的模式     
我想这取决于所需的精度,但您可以使用基于“整数”的浮点数。使用此方法,您使用整数并为小数位数提供自己的偏移量。 例如,精度为4小数点,你就可以得到 浮点值 - > int值 1.0000 - > 10000 1.0001 - > 10001 0.9999 - > 09999 当你进行乘法和除法时必须小心,并且在应用精确偏移时要小心。另外,你可以很快得到溢出错误。 1.0001 * 1.0001变为10001 * 10001/10000     
如果我正确读取了这段代码,任何两个相邻染色体片段之间的距离都不应该改变。在这种情况下,在主循环之前计算每对相邻点之间的距离,并且在主循环之后,如果需要,移动每个点以与前一点具有适当的距离。 您可能需要在主循环期间多次强制执行此约束,具体取决于具体情况。     
基本上,您需要避免从这些(不精确的)矩阵运算符中累积误差,并且在大多数应用程序中有两种主要方法。 不是将位置写为多次操作的初始位置,而是可以在N次操作后写出操作员将明确显示的内容。例如,假设您有一个位置x,并且您正在添加一个值e(您无法准确表示。)比计算x + = e好多了;大量的时间是计算x + EN;其中EN是一种更准确的方式来表示N次后操作发生的情况。您应该考虑是否有一些方法可以更准确地表示许多旋转的动作。 稍微更人为的是从您的新发现的点开始,并预测与旋转中心的预期半径之间的任何差异。这样可以保证它不会漂移(但不一定能保证旋转角度准确。)     

要回复问题请先登录注册