【C语言学习趣事】_33_关于C语言和C++语言中的取余数(求模)的计算_有符号和无符号数的相互转换问题

时间:2022-01-26 21:38:17

最近再次复习C++语言,用的教材是《C++ Primer》这本教材, 看到第二章的时候,里面有个问题困扰了我。

于是想上网查查怎么回事, 结果看了很久都没有得到一个满意的答案。

书上有这么一段话:当将一个超出数据类型取值范围的值赋值给这个类型的一个变量时,变量的值的结果由变量的

类型决定。

后面还有这么一段解释:

1、当接受值的变量类型为无符号类型时,  变量的值 =  超出变量范围的值 % 类型可以表示的数值的个数

Exp:

unsigned char nTest;
nTest = ;

那么这样nTest的值就可以用下面的方式进行计算:

  nTest = 343 % 256 = 343 - 256 = 87    //中间的343-256 是计算方便,实际上没有这种转换方式

我在RHEL中测试的结果如下:

[root@localhost cpp_src]# cat test.cpp
#include <iostream> int main()
{
unsigned char nTest;
unsigned int n;
nTest = ;
n=nTest; std::cout<<"nTest="<<nTest<<std::endl;
std::cout<<"n="<<n<<std::endl;
std::cout<<"343%256="<<%<<std::endl; return ;
}
[root@localhost cpp_src]# g++ test.cpp
test.cpp: In function ‘int main()’:
test.cpp:: 警告:大整数隐式截断为无符号类型
[root@localhost cpp_src]# ./a.out
nTest=W
n=
%=

  可以看到测试的时候,对截断数据进行了提示,而且最终得到的结果也是这么计算的。

  让我迷惑的不是上面的过程,上面的过程非常的自然。令我迷惑的是下面的情况:

    当接受值的变量类型为无符号型,而赋给它的值是一个负数,该如何处理。

Exp:

unsigned char nTest;
nTest = -;

  按照上面的说法:   nTest = -1 % 256 ;

问题就出在这个地方,对于负数的求余数,我们该怎么求呢?以前从来没注意这个问题,

这次就想一定要弄明白。

[root@localhost cpp_src]# cat test.cpp
#include <iostream> int main()
{
unsigned char nTest;
unsigned int n;
nTest = -;
n=nTest; std::cout<<"nTest="<<nTest<<std::endl;
std::cout<<"n="<<n<<std::endl;
std::cout<<"-1%256="<<-%<<std::endl; return ;
}

执行结果为:

[root@localhost cpp_src]# g++ test.cpp
[root@localhost cpp_src]# ./a.out
nTest=�
n=
-%=-

  经过这个地方的计算,我们并不能得到什么规律,但是我们发现一个问题:  n= 255 = 256 + (-1)

256 是unsigned char 可以表示的数值的个数(8bit可以表示256个数), 而 -1 是 -1 % 256 后得到的余数。

于是我就继续做了几个实验:

[root@localhost cpp_src]# cat test.cpp
#include <iostream> int main()
{
unsigned char nTest;
unsigned int n;
nTest = -;
n=nTest; std::cout<<"nTest="<<nTest<<std::endl;
std::cout<<"n="<<n<<std::endl;
std::cout<<"-367%256="<<-%<<std::endl; return ;
}

执行结果为:

[root@localhost cpp_src]# g++ test.cpp
test.cpp: In function ‘int main()’:
test.cpp:: 警告:大整数隐式截断为无符号类型
[root@localhost cpp_src]# ./a.out
nTest=�
n=
-%=-

分析:  n=145 = 256 + (-367%256)= 256 + (-111) = 145;

还有下面的实验:

#include <iostream>

int main()
{
unsigned char nTest;
unsigned int n;
nTest = -;
n=nTest; std::cout<<"nTest="<<nTest<<std::endl;
std::cout<<"n="<<n<<std::endl;
std::cout<<"-1000%256="<<-%<<std::endl; return ;
}

执行结果如下所示:

[root@localhost cpp_src]# g++ test.cpp
test.cpp: In function ‘int main()’:
test.cpp:: 警告:大整数隐式截断为无符号类型
[root@localhost cpp_src]# ./a.out
nTest=
n=
-%=-

分析也是:  n = 24 = 256 + (-1000 % 256) = 256 + (-232) = 24 ;

于是我得到一个结论:

  无符号变量的值 = 类型可以取值的个数 + (负数 % 类型可以取值的个数)

例如8bit的计算公式为:

无符号变量的值 = 256 + (负数 % 256 )

下面通过代码进行测试:

#include <iostream>

int main()
{
unsigned char nTest;
unsigned int n;
int i;
unsigned int j;
for(i=;i<=;i++)
{
nTest = -+i;
j=nTest;
n= +( (-+i) % ); std::cout<<"nTest="<<nTest<<std::endl;
std::cout<<"j="<<j<<std::endl;
std::cout<<"n="<<n<<std::endl<<std::endl;
} return ;
}

测试结果为:

[root@localhost cpp_src]# g++ test.cpp
[root@localhost cpp_src]# ./a.out
nTest=
j=
n= nTest=
j=
n= nTest=
j=
n= nTest=
j=
n= nTest=
j=
n= nTest=
j=
n= nTest=
j=
n= nTest=
j=
n= nTest=
j=
n= nTest=!
j=
n= nTest="
j=
n=

  通过上面的测试,可以证明我的猜想是正确的。

  接下来的问题如何求取这个余数,因为以前都没考虑过这个问题。我们这里再次来进行猜想:

我们知道 -1000  % 256 = - 232,而  1000/256*256 + (-1000) = -232

-1       % 256  = -1        而 1     /256*256 + (-1) = -1

因此我们这样进行猜想求模公式为:

负数 % 变量类型可以表示数的个数  = 负数 * (-1) / 变量类型可以表示数的个数 * 变量类型可以表示数的个数 + 负数

下面我们用代码进行测试,验证上面的公式是否正确。

#include <iostream>

int main()
{
int n,k;
for(int i=;i<=;i++)
{
n=(-+i)%;
k = (- + i)*(-)/* + (-+i); std::cout<<"n="<<n<<std::endl;
std::cout<<"k="<<k<<std::endl<<std::endl;
} return ;
}

执行结果如下

[root@localhost cpp_src]# g++ mod.cpp
[root@localhost cpp_src]# ./a.out
n=-
k=- n=-
k=- n=-
k=- n=-
k=- n=-
k=- n=-
k=- n=-
k=- n=-
k=- n=-
k=- n=-
k=- n=-
k=-

  可以发现我的猜想与实际的结果一样,因此可以确定这种方法是对的。

小结:

  1、 当用正数赋值给无符号数时,如果正数在无符号数表示的值范围内时直接赋值。

           当用正数赋值给无符号数时,如果正数不在无符号数表示的值范围, 即正数 》 表示的值的范围 , 

          得到的结果为:  无符号变量 = 正数的值 % 无符号数类型可以表示的值个数

      2、当用负数给无符号数赋值时, 

    无符号变量的值 = 类型可以取值的个数 + (负数 % 类型可以取值的个数)

  3、负数 % 正数 的求模公式

             负数 % 类型可以取值的个数  = 负数 * (-1) / 类型可以取值的个数* 类型可以取值的个数 + 负数

    结合上面的第二条和第三条就可以得到有符号数给无符号数赋值时的最终求值公式。

   无符号变量的值 =  类型可以取值的个数 + 负数 * (-1) / 类型可以取值的个数 * 类型可以取值的个数 + 负数

  可以利用上面的公式进行测试:

unsigned char n;
unsigned int k; n = -;
k=n;

程序的执行结果如下:

[root@localhost cpp_src]# g++ test.cpp
test.cpp: In function ‘int main()’:
test.cpp:: 警告:大整数隐式截断为无符号类型
[root@localhost cpp_src]# ./a.out
n=U
k=

用手代入公式计算:

  无符号变量的值 =  类型可以取值的个数 + 负数 * (-1) / 类型可以取值的个数 * 类型可以取值的个数 + 负数

n = 256 + (-171)* -1 / 256 * 256 + (-171)  = 85

结果正确。

    至此,本次问题的讨论到此结束,如果您有更好的计算方法,请您不吝赐教。

如果您觉得我说的不正确,也请您不吝赐教。