【问题标题】:C++ compound assignment and type conversion issueC++ 复合赋值和类型转换问题
【发布时间】:2019-02-23 03:01:34
【问题描述】:

我正在用 C++ 计算组合(15, 7)。

  1. 由于type promotion error,我首先使用了以下代码并得到了错误的答案。

    #include <iostream>
    
    int main()
    {
        int a = 15;
        double ans = 1;
        for(int i = 1; i <= 7; i++)
            ans *= (a + 1 - i) / i;
        std::cout << (int) ans;
    
        return 0;
    }
    

    输出: 2520

  2. 所以我将ans *= (a + 1 - i) / i; 更改为ans *= (double)(a + 1 - i) / i; 仍然得到错误的答案。

    #include <iostream>
    
    int main()
    {
        int a = 15;
        double ans = 1;
        for(int i = 1; i <= 7; i++)
            ans *= (double) (a + 1 - i) / i;
        std::cout << (int) ans;
    
        return 0;
    }
    

    输出: 6434

  3. 最后,我尝试了ans = ans * (a + 1 - i) / i,它给出了正确的答案。

    #include <iostream>
    
    int main()
    {
        int a = 15;
        double ans = 1;
        for(int i = 1; i <= 7; i++)
            ans = ans * (a + 1 - i) / i;
        std::cout << (int) ans;
    
        return 0;
    }
    

    输出: 6435

谁能告诉我为什么第二个不起作用?

【问题讨论】:

    标签: c++


    【解决方案1】:

    如果您打印出ans 而不将其转换为(int),您将看到第二个结果是6434.9999999999990905052982270717620849609375。这与6535 的正确答案非常接近,因此它显然不再是类型提升错误。

    不,这是经典的floating point inaccuracy。当你写 ans *= (double) (a + 1 - i) / i 时,你正在做相当于:

    ans = ans * ((double) (a + 1 - i) / i);
    

    将此与第三版进行比较:

    ans = ans * (a + 1 - i) / i;
    

    前者先除法,后乘法。后者从左到右运算,因此乘法先于除法。这种操作顺序的变化导致两者的结果略有不同。浮点计算对运算顺序极为敏感。

    快速修复:不要截断结果; round它。

    更好的解决方法:不要使用浮点数进行积分运算。保存除法,直到完成所有乘法运算。使用longlong long,甚至是big number library

    【讨论】:

      【解决方案2】:

      第一个不起作用,因为那里有整数除法。

      第二个和第三个的区别是这样的:

      ans = ans * (double(a + 1 - i) / i); // second is equal to this
      

      对比:

      ans = (ans * (a + 1 - i)) / i; // third is equal to this
      

      所以区别在于乘法和除法的顺序。如果将 double 舍入为整数而不是简单地删除小数部分,您将得到相同的结果。

      std::cout << int( ans + 0.5 ) << std::endl;
      

      【讨论】:

      • 我唯一的事情是,在我评论之后,我意识到结果只是 2. 和 3. of 1 之间的差异。我不认为这种类型的错误只会被关闭在这样的乘法中使用时为 1。
      • 它不是关闭 1,而是关闭 0.000000001,但是因为你删除了小数部分,所以你看不到。
      • 好的,我明白了。随着结转和截断等。我知道它看起来像减了 1,但不是真的。但我的意思是,差异不应该大于 1,而不是小于 1?
      猜你喜欢
      • 2020-04-19
      • 2022-08-19
      • 1970-01-01
      • 1970-01-01
      • 2017-10-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多