长话短说。如果给浮点数f赋值为2位小数,那么说f< = 0.20f和f< 0.21f?
好的,这是长篇故事:
我正在做一个练习,我必须找到多少硬币组合才能用给定的一组硬币回馈给顾客。
有一条规则,当限制设置为1时,每个只有20个硬币可用。
这是我的代码
int calcul(float change, int depth, int restriction)
{
float m[] = {2.0, 1.0, 0.5, 0.2, 0.1, 0.05, 0.02, 0.01};
int count = 0;
float coin = m[depth];
if (depth == 7 && (restriction == 0 || (restriction == 1 && change <= 0.20f)))
return 1;
else if (depth == 7)
return 0;
int i = 0;
while (coin * i <= change && (restriction == 0 || (restriction == 1 && i <= 20)))
{
if (coin * i == change)
count++;
else
count += calcul(rendu - coin * i, depth + 1, restriction);
i++;
}
return count;
}
如果深度等于7且限制设置为1,则我只能给出20 * 0.01。因此,只有在更改为0.20或更低时才可以进行操作。
if (depth == 7 && (restriction == 0 || (restriction == 1 && change <= 0.20f)))
我尝试使用2位小数的数字,但结果不是好的。然后我试着这样:
if (depth == 7 && (restriction == 0 || (restriction == 1 && change < 0.21f)))
并且有效。
答案 0 :(得分:1)
回答问题的初始问题:
f <= 0.20f
和f < 0.21f
彼此不同,都错了。
如果我理解这个问题,那么暗示f
是以某种方式计算出来的,如果使用的数据类型支持.01
所以f可能是偏高的.20的近似值,因此f <= 0.20f
会给你错误的&#34;回答f < 0.21f
是正确的。但是另一次,f可能是.21偏低的近似值,所以f <= 0.20f
给出了正确的答案,但f < 0.21f
在你想要假的时候给你真实。
如果你必须使用.01的倍数的中等差的近似值与分割差值的常数相比:f <= .205
但是你确定你的近似值只是中度差吗?也许他们是如此糟糕,以至于分裂差异失败了。
底线仍然是美分应该表示为整数美分而不是.01 *浮动数美元。
答案 1 :(得分:1)
当使用浮点数来计算精确数学时,就像使用金钱一样,考虑到FP数学的不精确性。
考虑每个货币价值最多接近最低单位的倍数(0.01)。
相应地改变代码
#define MIN_DENOMINATION 0.01f
float roundm(float x) {
return roundf(x/MIN_DENOMINATION) * MIN_DENOMINATION;
}
// change <= 0.20f
change <= (0.20f + MIN_DENOMINATION/2)
// coin * i <= change
coin * i <= (change + MIN_DENOMINATION/2)
// coin * i == change
roundm(coin * i) == roundm(change) // or
roundm(coin * i - change) == 0.0f
// rendu - coin * i
roundm(rendu - coin * i)
其他选择包括
使用小数点浮点(如果有)。
以最小货币单位缩放值。 (例如* 100)
使用double
的工作效果优于float
,但结果类似,只是更远。
使用缩放整数数学。在整数数学截断为0而不是舍入到最接近时要小心舍入。
答案 2 :(得分:0)
对于这些事情,浮点数永远不会100%准确 - 即使在0.10
中读取也会产生类似0.0999996
的值,因此它不再是完全正确的0.1。你做完一些数学计算之后别管它。如果您仍然必须比较不精确的值,那么方法是if ( abs(a - b) < epsilon ) ...
epsilon
,其中0.0001
是您不再相等的值的保证金 - 对于金钱,您可以使用0.001
或{ {1}}例如,好像它在一个百分之一或十分之一的范围内,它可能是正确的价值。
每当我必须在计划中处理资金时[价值实际上很重要,而不是“如果抵押贷款利率是3%,我可以用20000美元的工资借钱多少”类型的数学问题,其中一些分在这里或无论如何都没关系],我更喜欢使用整数值。
可以使用两个int的结构来表示它为“美元”和“分”(或“欧元”和“美分”,“磅”和“便士”,旧“磅”,“先令”和“便士“,”克朗“和”öre“,或者你使用的任何货币)。
我更喜欢使用整数将最小分数计为大数(“美分”,“便士”,“öre”等)作为一个整数值,因此1美元[etc]是值100 [显然,旧式的“磅,先令和便士”会使一种更有趣的分裂回“人类可读”的形式,但除此之外它是相同的原则]。这也是大多数人处理其他“棘手”单位的方式,例如使用秒来表示时间,而不是秒,小时,分钟,天等。并且日期被计为“从特定时间开始的秒数”,因此不是一堆不同的数字(1843年6月7日1843,6,7,你会得到大量的数字)。
特别是在具有64位整数的现代计算机中,这些东西是非常不受限制的,除非你计算土耳其里拉所有国家的国债,或者其他一些国家。
答案 3 :(得分:-1)
浮点数和双精度数以二进制形式存储在机器中。因此,虽然0.20看起来像一个很好的干净数字,但它实际上是二进制的重复小数。因此,机器中的数字不是完全0.20。它实际上非常,非常略高于或低于0.20,因此比较无法按预期运行。
具体来说,十进制中的0.20是二进制的0.0011001100110011 ...
要纠正这个问题,千万不要期望浮动或双重是完全的,只是近似,你就没事了。
所以,改变这个
change <= 0.20
到这个
change < 0.2001f
并且你在浮动的准确度范围内。
使用
时,原始问题中的代码会出现完全相同的问题change < 0.21
因为0.21 MIGHT的值落在0.21的下端,这将评估为真。这是因为您使用的精度范围为0.01,这对于您遇到的数据来说还不够好。