signed int modulo unsigned int产生无意义的结果

时间:2017-04-08 11:45:54

标签: c modulo mod signedness

我需要在C中执行一个真正的数学模数。对于我来说,允许模数参数的负数是有意义的,因为我的模块化计算会产生负中间结果,必须将其放回到最少残留系统中。但允许负面模块是没有意义的,因此我写了

printf("%u\n", mod(-3, 11));

然而,使用负数和正模块调用此类函数

1

产生输出

using System;

namespace BankAccounts  
{  
   class Account  
   {  
       protected Account(decimal balance)  
       { _Balance = balance; }  

       private decimal _Balance;
       public decimal Balance
       {
           get { return _Balance; }
       }

       public override string ToString()
       {
           return string.Format("Balance: {0:c}", _Balance);
       }
   }
}

我不明白为什么。你能解释一下吗?

编辑:我知道operator%与数学模数不同,我知道它是如何为正数和负数定义的。我问的是它会为不同的签名做些什么,而不是不同的标志。

3 个答案:

答案 0 :(得分:5)

启用clang

-Wconversion可以明确指出您的错误:

prog.cc:3:15: warning: implicit conversion changes signedness: 'unsigned int' to 'int' [-Wsign-conversion]
    int r = x % m;
        ~   ~~^~~
prog.cc:3:13: warning: implicit conversion changes signedness: 'int' to 'unsigned int' [-Wsign-conversion]
    int r = x % m;
            ^ ~
prog.cc:4:21: warning: operand of ? changes signedness: 'int' to 'unsigned int' [-Wsign-conversion]
    return r >= 0 ? r : r + m;
    ~~~~~~          ^
prog.cc:4:25: warning: implicit conversion changes signedness: 'int' to 'unsigned int' [-Wsign-conversion]
    return r >= 0 ? r : r + m;
                        ^ ~
prog.cc:9:12: warning: implicit conversion changes signedness: 'unsigned int' to 'int' [-Wsign-conversion]
    return mod(-3, 11);
    ~~~~~~ ^~~~~~~~~~~

live example on wandbox

转换为unsigned int时,-3变为4294967293

4294967293 % 11等于1

答案 1 :(得分:2)

见C11 6.5.5(乘法运算符)/ 3:

  

通常的算术转换是在操作数上执行的。

usual arithmetic conversions由6.3.1.8定义。相关部分是:

  

否则,如果具有无符号整数类型的操作数的等级大于或等于   等于另一个操作数的类型的等级,然后是操作数   有符号整数类型转换为带有unsigned的操作数的类型   整数类型。

因此,在void PerformTransformation(Gdiplus::Bitmap* bitmap, LPCTSTR SaveFileName) { Gdiplus::BitmapData* bitmapData = new Gdiplus::BitmapData; UINT Width = bitmap->GetWidth(); UINT Height = bitmap->GetHeight(); Gdiplus::Rect rect(0, 0,Width,Height ); // Lock a 5x3 rectangular portion of the bitmap for reading. bitmap->LockBits(&rect, Gdiplus::ImageLockModeWrite, PixelFormat32bppARGB, bitmapData); byte* Pixels = (byte*)bitmapData->Scan0; INT stride_bytes_count = abs(bitmapData->Stride); UINT row_index, col_index; byte pixel[4]; for (col_index = 0; col_index < Width; ++col_index) { for (row_index = 0; row_index < Height; ++row_index) { unsigned int curColor = Pixels[row_index * stride_bytes_count / 4 + col_index]; int b = curColor & 0xff; int g = (curColor & 0xff00) >> 8; int r = (curColor & 0xff0000) >> 16; if ((r + 10) > 255) r = 255; else r += 10; if ((g + 10) > 255) g = 255; else g += 10; if ((b + 10) > 255) b = 255; else b += 10; pixel[0] = b; pixel[1] = g; pixel[2] = r; Pixels[row_index * stride_bytes_count / 4 + col_index] = *pixel; } } bitmap->UnlockBits(bitmapData); ::DeleteObject(bitmapData); CLSID pngClsid; GetEncoderClsid(L"image/png", &pngClsid); bitmap->Save(SaveFileName, &pngClsid, NULL); } }; 中,x % m首先转换为unsigned int。

要避免此行为,您可以使用x,但如果x % (int)m这会出现故障。如果您想支持m > INT_MAX以及否定m > INT_MAX,则必须使用稍微复杂的逻辑。

答案 2 :(得分:0)

其他答案很好解释OP在unsigned操作没有产生预期结果之前将负值转换为%时遇到了麻烦。

以下是解决方案:一个采用更广泛的数学(可能并不总是可用)。第二个是小心构造,以避免任何未定义的行为,(UB),实现定义(ID)行为或仅使用int, unsigned数学的溢出。它不依赖于2的补码。

unsigned int mod_ref(int x, unsigned int m) {
  long long r = ((long long) x) % m;
  return (unsigned) (r >= 0 ? r : r + m);
}

unsigned int mod_c(int x, unsigned int m) {
  if (x >= 0) {
    return ((unsigned) x) % m;
  }
  unsigned negx_m1 = (unsigned) (-(x + 1));
  return m - 1 - negx_m1 % m;
}

测试驱动程序

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>

void testm(int x, unsigned int m) {
  if (m) {
    unsigned r0 = mod_ref(x, m);
    unsigned r1 = mod_c(x, m);
    if (r0 != r1) {
      printf("%11d %10u --> %10u %10u\n", x, m, r0, r1);
    }
  }
}

int main() {
  int ti[] = {INT_MIN, INT_MIN + 1, INT_MIN / 2, -2, -1, 0, 1, 2, INT_MAX / 2,
      INT_MAX - 1, INT_MAX};
  unsigned tu[] = {0, 1, 2, UINT_MAX / 2, UINT_MAX - 1, UINT_MAX};
  for (unsigned i = 0; i < sizeof ti / sizeof *ti; i++) {
    for (unsigned u = 0; u < sizeof tu / sizeof *tu; u++) {
      testm(ti[i], tu[u]);
    }
  }
  for (unsigned i = 0; i < 1000u * 1000; i++) {
    int x = rand() % 100000000;
    if (rand() & 1)
      x = -x - 1;
    unsigned m = (unsigned) rand();
    if (rand() & 1)
      m += INT_MAX + 1u;
    testm(x, m);
  }
  puts("done");
  return 0;
}