无符号数与有符号数交叉运算
在一般的混合运算中,如果存在有符号数与无符号数交织运算,这个时候必须主要计算结果的符号位转换,下面举例说明一下:
#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;
}
结论
针对有符号与无符号混合运算时:
- 所有变量均为有符号数时,结果为有符号数
- 只有要一个变量为无符号数时,其结算结果自动转换为无符号数。
浮点运算中整型与浮点型变量之间的转换问题
两个整形变量相除得到的浮点型结果会将小数部分截断,例子如下
#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
总结:
- 当两个32位整型数相乘赋值给一个64位整型数时一定要将其中一个32位整型强制转换为64位整型
- 使用define定义数值型常量时,不要忘记在末尾标记ULL或LL