继续有符号与无符号混合运算

前面记录一篇,关于有符号与无符号的混合运算,会有一些意想不到的结果

那么,我们是否可以“负负得正”呢?

什么情况下是不行的?

 

首先,不要被绕晕。

这是前提: 所有的无符号与有符号运算,都是先转成无符号运算的。结果返回 无符号,

 

那么,理论结果如果本身就是正数的,那实际就应该是正常的

如果结果不是正数的,那么是负数的无符号形式(最高位=1,其他位取反)

 

为了验证效果,(就验证加/减/乘/除),写段代码试试:

    int a = -20;
    unsigned int b = 10;

    //保持原始结果
    cout << "a+b=" << a + b << endl;    //希望:-10
    cout << "a*b=" << a * b << endl;     //希望:-200
    cout << "a-b=" << a - b << endl;       //希望:-30
    cout << "a/b=" << a / b << endl;       //希望:-2

    cout << "-a-b=" << -a - b << endl;     //希望:10
    cout << "-b+a=" << -b + a << endl;   //希望:-30

    //对结果进行强转
    cout << "a+b=" << (int)(a + b) << endl;
    cout << "a*b=" << (int)(a * b) << endl;
    cout << "a-b=" << (int)(a - b) << endl;
    cout << "a/b=" << (int)(a / b) << endl;

    cout << "-a-b=" << (int)(-a - b) << endl;
    cout << "-b+a=" << (int)(-b + a) << endl;

 

实际情况呢?

 

 

a+b是个无符号的结果,通过强转之后,是-10,可以负负得正,一般可控范围

a*b是个无符号的结果,通过强转之后,是-200,可以负负得正,一般可控范围

a-b是个无符号的结果,通过强转之后,是-30,可以负负得正,一般可控范围

a/b是个无符号的结果,通过强转之后,是429496727,不可以负负得正,属于不可控范围。

 

那么为什么只有除法不行了呢?

这个还是得从原理讲起,首先,乘法 跟 除法 都是通过移位来解决的

乘法是 左移

除法是 右移

 

然后,再看下,负数表达形式(以32位系统为例)。

-32 如果是无符号的话,是这样的:

 

 

如果是64bit,那么前面再多8个F

 

运算乘法 与 除法 之前,都先转 成 无符号的。

 

乘法,左移,之后,最高位仍然为1,当然前提数据不能溢出的情况,此时如果对结果再进行强转回来,至少符号位还是正常的

除法,右移,之后,最高位补0,那么就丢失了符号位,哪怕对结果再进行强转回来,也是由于缺少符号位,导致最后数据仍然不对!

 

 

int a = -32;
unsigned int b = 16;

//此时会输出什么
a += b;
cout << "a += b   a=" << a << endl;    

//此时会输出什么
a /= b;
cout << "a /= b   a=" << a << endl;

 

 实际结果:

 

 解释:

a是有符号,b是无符号,先a要转换成无符号,然后再跟b计算,最后由于a是有符号的,结果又转换成有符号,结果符合预期

第二种情况:也是这个步骤,只不过除法用移位方式实现,一旦最高位移掉之后,就不是想要的数据

 

-a - b,是个另类,-a的优先级,要高于 “减法” 运算符,因此,要先处理 -a 而不是先转成 无符号

           那也说得通:-a = 20, 无符号之后,仍然 20  ;20 - 10 = 10 ,没毛病

 

最后结论:

1. 如果没有除法的话,无符号跟有符号运算,可以仅对结果进行强转。(前提结果不能溢出)(不推荐这样做,推荐第2种做法)

2. 如果有除法,必须先给无符号转换成有符号的,再计算。

 

补充下,实际代码的一些风险点:

 

 

这个是没有问题的,但风格不好

 

posted @ 2021-03-08 15:30  小刚学长  阅读(374)  评论(0)    收藏  举报