符号运算编程总结

无符号数与有符号数交叉运算

在一般的混合运算中,如果存在有符号数与无符号数交织运算,这个时候必须主要计算结果的符号位转换,下面举例说明一下:

#include <stdio.h>

int main(void)
{
    unsigned int a = 2;
    int b = -20;

    if(a + b > 6)
    {
        printf("%s\n", "a+b > 6");
    }
    else
    {
        printf("%s\n", "a+b < 6");
    }

    return 0;
}

上面的输出结果为:
a+b > 6

因为上面有符号的a与无符号的b之间做混合运算,系统默认转换为了无符号数,导致上面的运算错误,所以当你存在混合算数运算时一定要非常小心,这些错误不易发现,且非常消耗时间。

下面再举例说明一下:

#include <iostream>

int main(void)
{
    uint32_t num1 = 2;
    uint32_t num2 = 3;
    int32_t num3 = (num1-num2)/num1;
    std::cout << num3 << std::endl;
}

上面的输出结果为:
2147483647

上面的运算正确结果应该num3应该为0,原本应该为-0.5的,但由于num3为整形,所以截断之后应该为0,但是输出结果却是2147483647,这个错误同样是由于有符号与无符号转换引发的计算错误,正确的修改应该为:

#include <iostream>

int main(void)
{
    uint32_t num1 = 2;
    uint32_t num2 = 3;
    int32_t num3 = (int32)(num1-num2)/(int32)num1;
    std::cout << num3 << std::endl;
}

结论

针对有符号与无符号混合运算时:

  1. 所有变量均为有符号数时,结果为有符号数
  2. 只有要一个变量为无符号数时,其结算结果自动转换为无符号数。

浮点运算中整型与浮点型变量之间的转换问题

两个整形变量相除得到的浮点型结果会将小数部分截断,例子如下

#include <iostream>

int main(void)
{
    uint32_t num1 = 2;
    uint32_t num2 = 3;
    double f3 = num2/num1;
    std::cout << f3 << std::endl;
}

输出:
1.0000000

所以我们可以将其改为

#include <iostream>

int main(void)
{
    uint32_t num1 = 2;
    uint32_t num2 = 3;
    double f3 = num2/(num1*1.0);
    std::cout << f3 << std::endl;
}

浮点数网络传输问题

在某些场景中,我们需要在网络中传输浮点型参数,比如我们传递的参数为0.0000123456789,该如何传递?

如果遇到这个问题,我们其实可以将浮点数的整体放大e15倍之后进行传输,在网络上传输的数均是以e-15(即10^-15)为单位,实际的精度可以自由控制。

#include <iostream>
#include <cmath>
#define UNIT 10000000000000000LL
int main(void)
{
    uint32_t num1 = 2;
    uint32_t num2 = 3;
    double f3 = num1/(num2*1.0);
    std::cout << f3 << std::endl;
    uint64_t fSendInNet = f3*UNIT;
    std::cout << fSendInNet << std::endl;

    // todo: send to network

    double fRcvInNet = fSendInNet/(UNIT*1.0);
    std::cout << fRcvInNet << std::endl;
}

32位整型到64位整型的精度提升问题

在实际的编程中,经常会遇到两整型相乘得到另一个整型数的场景,如果两个乘数为32位,相乘之后的结果也没有超过32位,那么就不会有任何问题,但是如果两个乘数的结果溢出的话,那么就需要将结果存储在64位的变量中,这里也会有一个很容易出问题的点,我们来看一下

#include <iostream>
#include <cmath>

int main(void)
{
    uint32_t num1 = 0xffffffff;
    uint32_t num2 = 2;
    
    uint64_t num3 = num1*num2;
    std::cout << std::hex << num3 << std::endl;
}

输出结果为:
fffffffe

很明显结果不符合预期,两个32位整形运算溢出之后直接将溢出后的结果赋值给了num3,这显然是错误的,那我现在将其修改一下,类型转换一下

#include <iostream>
#include <cmath>

int main(void)
{
    uint32_t num1 = 0xffffffff;
    uint32_t num2 = 2;
    
    uint64_t num3 = (uint64_t)(num1*num2);
    std::cout << std::hex << num3 << std::endl;
}

输出结果为:
fffffffe

输出结果没有变化,这是为什么呢?我已经做了类型转换了?实际上这个是将计算之后的结果做32位到64位的转换,当结果已经计算完毕之后这样的转换已经没有效果,所以我们应该这样修改:

#include <iostream>
#include <cmath>

int main(void)
{
    uint32_t num1 = 0xffffffff;
    uint32_t num2 = 2;
    
    uint64_t num3 = (uint64_t)num1*num2;
    std::cout << std::hex << num3 << std::endl;
}

输出结果为:
1fffffffe

这个结果就对了,所以当遇到大数作乘法运算时一定要注意精度转换与溢出问题,所以很多时候当我们定义一个大数时喜欢在其后使用ULL或者LL来标识long long类型,比如

#define UNIT 100000000000000ULL

总结:

  1. 当两个32位整型数相乘赋值给一个64位整型数时一定要将其中一个32位整型强制转换为64位整型
  2. 使用define定义数值型常量时,不要忘记在末尾标记ULL或LL

版权声明:本文为dean_zhang5757原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。