在流体模拟过程中,物理时间正在进行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;
为什么会这样发生?
如果你们能够容忍我一点点,我可以大声喊:“C ++委员会可以对此做点什么吗?因为这令人困惑。” :)
获得正确余数以避免截止误差影响的最佳方法是什么? fmod是更好的解决方案吗?感谢
David Schwartz
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;
我得到了两个,
2/3 = 0.666667
2*(1/3) = 0.666667
答案 0 :(得分:7)
浮点值无法准确表示每个可能的数字,因此您的数字是近似的。在计算中使用时会产生不同的结果。
如果需要比较浮点数,则应始终使用小的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
您将看到两个输入值都没有精确表示,每个输入值都被向上舍入或向下舍入到最接近的值。 org
已向下舍入,因为序列中的下一位将为0. ddd
已向上舍入,因为该序列中的下一位将为1。
因此,在执行数学运算时,舍入可以取消或累加,具体取决于操作以及原始数字的舍入方式。
在这种情况下,1 / 0.1碰巧整齐地回到正好10。
将org
乘以10恰好会向上舍入。
将org
划分为ddd
恰好向下舍入(我说'碰巧',但你将一个向下舍入的数字除以一个向上舍入的数字,所以结果很自然少了)。
不同的输入将以不同的方式进行。
这只是一个误差,即使是一个微小的epsilon也很容易被忽略。
答案 1 :(得分:1)
如果我正确理解你的问题,那就是:为什么,有限精度算术,X/Y
不一样X * (1/Y)
?
原因很简单:例如,考虑使用六位十进制精度。虽然这不是double
实际做的,但概念完全相同。
六位小数,1/3
为.333333
。但2/3
是.666667
。所以:
2 / 3 = .666667
2 * (1/3) = 2 * .333333 = .6666666
这就是固定精度数学的本质。如果您无法容忍此行为,请不要使用有限精度类型。
答案 2 :(得分:0)
您无法准确代表4.6
:http://www.binaryconvert.com/result_double.html?decimal=052046054
在分离整数和分数部分之前使用舍入。
<强>更新强>
您可能希望使用Boost库中的rational
类:http://www.boost.org/doc/libs/1_52_0/libs/rational/rational.html
关于你的任务
要查找所需的double
精确度,例如,要查找4.6
计算“亲近度”:
double time;
...
double epsilon = 0.001;
if( abs(time-4.6) <= epsilon ) {
// found!
}
答案 3 :(得分:0)
我不确定你想要达到什么目标,但如果你想得到一个价值然后想要 在1/1000的范围内做一些改进,为什么不使用整数而不是浮点数/双打?
你有一个除数,即1000,并且你有迭代的值,你需要乘以除数。
所以你会得到像
这样的东西double org = ... // comes from somewhere
int divisor = 1000;
int referenceValue = org * div;
for (size_t step = referenceValue - 10; step < referenceValue + 10; ++step) {
// use (double) step / divisor to feed to your algorithm
}