浮动比较越来越少或相等

时间:2015-11-16 21:34:46

标签: c

长话短说。如果给浮点数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)))

并且有效。

4 个答案:

答案 0 :(得分:1)

回答问题的初始问题:

f <= 0.20ff < 0.21f彼此不同,都错了

如果我理解这个问题,那么暗示f是以某种方式计算出来的,如果使用的数据类型支持.01

的精确倍数,则会生成.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)

其他选择包括

  1. 使用小数点浮点(如果有)。

  2. 以最小货币单位缩放值。 (例如* 100)

  3. 使用double的工作效果优于float,但结果类似,只是更远。

  4. 使用缩放整数数学。在整数数学截断为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,这对于您遇到的数据来说还不够好。