在我的流体模拟过程中,物理时间正在进行中0, 0.001, 0.002, ..., 4.598, 4.599, 4.6, 4.601, 4.602, ....现在我想0.1, 0.2, ..., 4.5, 4.6, ...从这个时间序列中选择时间= 然后进行进一步的分析.所以我写了下面的代码来判断fractpart命中是否为零.
但我很惊讶我发现以下两种分割方法得到两种不同的结果,我该怎么办?
double param, fractpart, intpart;
double org = 4.6;
double ddd = 0.1;
// This is the correct one I need. I got intpart=46 and fractpart=0
// param = org*(1/ddd);
// This is not what I want. I got intpart=45 and fractpart=1
param = org/ddd;
fractpart = modf(param , &intpart);
Info<< "\n\nfractpart\t=\t"
<< fractpart
<< "\nAnd intpart\t=\t"
<< intpart
<< endl;
Run Code Online (Sandbox Code Playgroud)
为什么会这样发生?
如果你们能够容忍我一点点,我可以大声喊:"C++委员会可以对此做点什么吗?因为这令人困惑." :)
获得正确余数以避免截止误差影响的最佳方法是什么?fmod是更好的解决方案吗?谢谢
大卫施瓦茨
double aTmp = 1;
double bTmp = 2;
double cTmp = 3;
double AAA = bTmp/cTmp;
double BBB = bTmp*(aTmp/cTmp);
Info<< "\n2/3\t=\t"
<< AAA
<< "\n2*(1/3)\t=\t"
<< BBB
<< endl;
Run Code Online (Sandbox Code Playgroud)
我得到了两个,
2/3 = 0.666667
2*(1/3) = 0.666667
Run Code Online (Sandbox Code Playgroud)
浮点值无法准确表示每个可能的数字,因此您的数字是近似的.当在计算中使用时,这导致不同的结果.
如果需要比较浮点数,则应始终使用小的epsilon值而不是测试相等性.在你的情况下,我将舍入到最接近的整数(不是向下舍入),从原始值中减去该值,并将结果的abs()与epsilon进行比较.
如果问题是,为什么总和不同,简单的答案是它们是不同的总和.有关更长的解释,以下是所涉及数字的实际表示:
org: 4.5999999999999996 = 0x12666666666666 * 2^-50
ddd: 0.10000000000000001 = 0x1999999999999a * 2^-56
1/ddd: 10 = 0x14000000000000 * 2^-49
org * (1/ddd): 46 = 0x17000000000000 * 2^-47
org / ddd: 45.999999999999993 = 0x16ffffffffffff * 2^-47
Run Code Online (Sandbox Code Playgroud)
您将看到,两个输入值都没有精确表示,每个输入值都被向上舍入或向下舍入到最接近的值.org已向下舍入,因为序列中的下一位将为0. ddd已向上舍入,因为该序列中的下一位将为1.
因此,当执行数学运算时,舍入可以取消或累加,这取决于操作以及原始数字如何舍入.
在这种情况下,1/0.1碰巧整齐地回到正好10.
乘以org10恰好向上舍入.
除org通过ddd发生在本轮下跌(我说"碰巧",但你除以一个舍入数下舍入数,所以很自然的结果是更小).
不同的输入将以不同方式进
它只是一个误差,即使是一个微小的epsilon也很容易被忽略.