【C語言學習趣事】_33_關於C語言和C++語言中的取余數(求模)的計算_有符號和無符號數的相互轉換問題


     最近再次復習C++語言,用的教材是《C++ Primer》這本教材, 看到第二章的時候,里面有個問題困擾了我。

於是想上網查查怎么回事, 結果看了很久都沒有得到一個滿意的答案。

    書上有這么一段話:當將一個超出數據類型取值范圍的值賦值給這個類型的一個變量時,變量的值的結果由變量的

類型決定。

   后面還有這么一段解釋:

   1、當接受值的變量類型為無符號類型時,  變量的值 =  超出變量范圍的值 % 類型可以表示的數值的個數

Exp:

unsigned char nTest;
nTest = 343;

那么這樣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 = 343;
    n=nTest;

    std::cout<<"nTest="<<nTest<<std::endl;
    std::cout<<"n="<<n<<std::endl;
    std::cout<<"343%256="<<343%256<<std::endl;

    return 0;
}
[root@localhost cpp_src]# g++ test.cpp 
test.cpp: In function ‘int main()’:
test.cpp:7: 警告:大整數隱式截斷為無符號類型
[root@localhost cpp_src]# ./a.out 
nTest=W
n=87
343%256=87

  可以看到測試的時候,對截斷數據進行了提示,而且最終得到的結果也是這么計算的。

  讓我迷惑的不是上面的過程,上面的過程非常的自然。令我迷惑的是下面的情況:

    當接受值的變量類型為無符號型,而賦給它的值是一個負數,該如何處理。

Exp:

unsigned char nTest;
nTest = -1;

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

     問題就出在這個地方,對於負數的求余數,我們該怎么求呢?以前從來沒注意這個問題,

這次就想一定要弄明白。

[root@localhost cpp_src]# cat test.cpp 
#include <iostream>

int main()
{
    unsigned char nTest;
    unsigned int  n;
    nTest = -1;
    n=nTest;

    std::cout<<"nTest="<<nTest<<std::endl;
    std::cout<<"n="<<n<<std::endl;
    std::cout<<"-1%256="<<-1%256<<std::endl;

    return 0;
}

執行結果為:

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

  經過這個地方的計算,我們並不能得到什么規律,但是我們發現一個問題:  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 = -367;
    n=nTest;

    std::cout<<"nTest="<<nTest<<std::endl;
    std::cout<<"n="<<n<<std::endl;
    std::cout<<"-367%256="<<-367%256<<std::endl;

    return 0;
}

執行結果為:

[root@localhost cpp_src]# g++ test.cpp 
test.cpp: In function ‘int main()’:
test.cpp:7: 警告:大整數隱式截斷為無符號類型
[root@localhost cpp_src]# ./a.out 
nTest=�
n=145
-367%256=-111

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

還有下面的實驗:

#include <iostream>

int main()
{
    unsigned char nTest;
    unsigned int  n;
    nTest = -1000;
    n=nTest;

    std::cout<<"nTest="<<nTest<<std::endl;
    std::cout<<"n="<<n<<std::endl;
    std::cout<<"-1000%256="<<-1000%256<<std::endl;

    return 0;
}

執行結果如下所示:

[root@localhost cpp_src]# g++ test.cpp 
test.cpp: In function ‘int main()’:
test.cpp:7: 警告:大整數隱式截斷為無符號類型
[root@localhost cpp_src]# ./a.out 
nTest=
n=24
-1000%256=-232

分析也是:  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=0;i<=10;i++)
    {
        nTest = -1000+i;
        j=nTest;
        n=256 +( (-1000+i) % 256);

        std::cout<<"nTest="<<nTest<<std::endl;
        std::cout<<"j="<<j<<std::endl;
        std::cout<<"n="<<n<<std::endl<<std::endl;
    }

    return 0;
}

測試結果為:

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

nTest=
j=25
n=25

nTest=
j=26
n=26

nTest=
j=27
n=27

nTest=
j=28
n=28

nTest=
j=29
n=29

nTest=
j=30
n=30

nTest=
j=31
n=31

nTest= 
j=32
n=32

nTest=!
j=33
n=33

nTest="
j=34
n=34

  通過上面的測試,可以證明我的猜想是正確的。

  接下來的問題如何求取這個余數,因為以前都沒考慮過這個問題。我們這里再次來進行猜想:

我們知道 -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=0;i<=10;i++)
    {
        n=(-1000+i)%256;
        k = (-1000 + i)*(-1)/256*256 + (-1000+i);

        std::cout<<"n="<<n<<std::endl;
        std::cout<<"k="<<k<<std::endl<<std::endl;
    }

    return 0;
}

執行結果如下

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

n=-231
k=-231

n=-230
k=-230

n=-229
k=-229

n=-228
k=-228

n=-227
k=-227

n=-226
k=-226

n=-225
k=-225

n=-224
k=-224

n=-223
k=-223

n=-222
k=-222

  可以發現我的猜想與實際的結果一樣,因此可以確定這種方法是對的。

 

小結:

  1、 當用正數賦值給無符號數時,如果正數在無符號數表示的值范圍內時直接賦值。

           當用正數賦值給無符號數時,如果正數不在無符號數表示的值范圍, 即正數 》 表示的值的范圍 , 

          得到的結果為:  無符號變量 = 正數的值 % 無符號數類型可以表示的值個數

      2、當用負數給無符號數賦值時, 

    無符號變量的值 = 類型可以取值的個數 + (負數 % 類型可以取值的個數)

  3、負數 % 正數 的求模公式

             負數 % 類型可以取值的個數  = 負數 * (-1) / 類型可以取值的個數* 類型可以取值的個數 + 負數

 

    結合上面的第二條和第三條就可以得到有符號數給無符號數賦值時的最終求值公式。

   無符號變量的值 =  類型可以取值的個數 + 負數 * (-1) / 類型可以取值的個數 * 類型可以取值的個數 + 負數

 

  可以利用上面的公式進行測試:   

unsigned char n;
unsigned int k;

n = -171;
k=n;

程序的執行結果如下:

[root@localhost cpp_src]# g++ test.cpp 
test.cpp: In function ‘int main()’:
test.cpp:8: 警告:大整數隱式截斷為無符號類型
[root@localhost cpp_src]# ./a.out 
n=U
k=85

用手代入公式計算:

  無符號變量的值 =  類型可以取值的個數 + 負數 * (-1) / 類型可以取值的個數 * 類型可以取值的個數 + 負數

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

結果正確。

     

    至此,本次問題的討論到此結束,如果您有更好的計算方法,請您不吝賜教。

如果您覺得我說的不正確,也請您不吝賜教。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM