【问题标题】:c++ Integer overflow in spite of using unsigned int and modulo operationsc++ 整数溢出,尽管使用无符号整数和模运算
【发布时间】:2021-07-23 09:39:47
【问题描述】:
        int orders=1000000000;
        int mod=pow(10,9)+7;
        unsigned int total=4294967295; 
        total=(orders*(orders+1))%mod;
        total/=2;
        return total;       

预期答案= 21

但是得到

运行时错误:有符号整数溢出:1000000000 * 1000000001 不能用“int”类型表示

我也试过

        int orders=1000000000;
        int mod=pow(10,9)+7;
        long long total=0LL; 
        total=(long long)(orders*(orders+1))%mod;
        total/=2;
        return total; 

同样的错误

//使用最新的C++ 17标准使用clang 11编译。

我可以知道为什么会这样吗? 我想也许 long long 被截断为 int 因此为 0LL, 但这仍然没有解决问题。

在其他编译器上尝试时, 获取输出

对于第一个代码:

-243309312

第二段代码:

1904174336

【问题讨论】:

  • 不要在调查整数行为的程序中使用pow;因为在查看您的问题之前,我们首先必须排除浮点问题。为什么不直接输入整数常量来替换 pow(10,9)
  • orders 是一个int(orders*(orders+1))%mod 都是ints
  • (orders*(orders+1))%mod; 中没有无符号整数,所以计算是有符号的。赋值运算符的 LHS 上的结果类型不参与 RHS 上表达式的类型转换,直到表达式被计算并且结果即将被赋值。注意(a * b) mod m == ((a mod m) * (b mod m)) mod m
  • 您尝试将两个有符号的 32 位整数相乘,每个整数都是 10⁹。
  • 你需要学习模运算来解决它而不会溢出。

标签: c++ c++11 integer-overflow


【解决方案1】:

基本上问题是在乘法之后整数溢出。

要克服这个问题,您必须使用两种解决方案之一来处理主题。

  • 使用整数类型,它可以保存如此大的结果,例如:unsigned long long int
  • 使用能够在没有整数溢出的情况下进行计算的算法 (为此我找不到好的简单的英文文档)

使用第二种方法:

constexpr unsigned mutiplyModulo(unsigned a, unsigned b, unsigned n)
{
    unsigned m = 1, w = 1;

    while (m) {
        if (b & m)
            w = (w + a) % n;
        a = (a << 1) % n;
        m <<= 1;
    }
    return w;
}

constexpr unsigned int intpow(unsigned int x, unsigned int n)
{
    unsigned int r = 1;
    while (n) {
        if (n & 1)
            r *= x;
        x *= x;
        n /= 2;
    }
    return r;
}

unsigned int foo(unsigned int orders)
{
    constexpr auto mod = intpow(10u, 9u) + 7u;
    unsigned int total = mutiplyModulo(orders, orders + 1, mod);
    total /= 2;
    return total;
}

https://godbolt.org/z/ePW7WxcTe

【讨论】:

    【解决方案2】:

    考虑大小为4字节的整数范围

    -2,147,483,648 to 2,147,483,647
    
    
    Unsigned int max  4,294,967,295
    

    现在声明

    total=(orders*(orders+1))%mod;
    
    

    将尝试将 orders*(orders+1) 放入已签名且最大值为“2,147,483,647”的 int 类型,因为 orders 是 int 类型。

    所以乘法结果

    1000000001000000000

    并且它不在 int 范围内。因此您在这里遇到溢出问题。

    为了更好的可见性运行这个

    int orders=1000000000;
            int mod=pow(10,9)+7;
            unsigned int total=4294967295; 
            total=(orders*(orders+1))%mod;
            total/=2;
            cout<< "result= "<<total<<"\n"; 
            int val = (orders*(orders+1));
            std::cout<<"val = "<<val;
            unsigned int t=(-486618624)%mod;
            std::cout<<"\nresult 2 = "<<t/2;
    
    

    【讨论】:

      猜你喜欢
      • 2013-04-10
      • 2014-12-09
      • 1970-01-01
      • 2012-02-29
      • 2021-03-15
      • 1970-01-01
      • 2023-03-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多