有符号数与无符号数之间运算问题探究

Posted 流楚丶格念

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了有符号数与无符号数之间运算问题探究相关的知识,希望对你有一定的参考价值。


无符号和有符号两个数比较时,会默认转成无符号数,再进行比较。

有符号数与无符号数之间运算问题

这个问题测试是否懂得C语言中的整数自动转换原则,有些开发者懂得极少这些东西。当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此,从这个意义上讲,无符号数的运算优先级要高于有符号数,这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。

例子:

首先我们先看一个例子,分别定义一个signed int型数据和unsigned int型数据,然后进行大小比较:

unsigned int a=20;
signed int b=-130;

if (a>b)
    printf("a>b\\n");
else    
    printf("b>a\\n");

是a>b?还是b>a?

我们看一下输出结果

b>a

结果证明b>a,也就是说-130>20,为什么会出现这样的结果呢?

这是因为在C语言操作中,如果遇到无符号数与有符号数之间的操作,编译器会自动转化为无符号数来进行处理,因此a=20,b=4294967166,这样比较下去当然b>a了。

而对于浮点数来说,浮点数(float,double)实际上都是有符号数,unsigned 和signed前缀不能加在float和double之上,当然就不存在有符号数根无符号数之间转化的问题了。

问题探究:

unsigned int变量跟int类型的变量运算时得出的结果是什么类型的?

1、先看一个有趣的例子:

#include<stdio.h>
#include<iostream>

using namespace std;

int main()
{
	int a = 3;
	unsigned int b = -6;
	printf("有符号数输出:%d\\n", a + b);
	printf("无符号数输出:%ud\\n", a + b);
	if ((a + b) > 0)
	{
		printf("yes\\n");
	}
	else
	{
		printf("no\\n");
	}
	return 0;
}

运行结果:

看到这个运行结果,很多小伙伴开始疑惑了?unsigned int类型怎么可以赋值一个-6?既然打印出来结果是-3<0,为什么还打印出来是yes?有这些问题的小伙伴且听我娓娓道来。

逐步思考:

int和unsigned int 在运算时候会应用到下面的各个原理

① 首先是 int类型和unsigned int类型的差别。

unsigned int类型比int类型的容量大是因为,unsigned int是无符号类型的,所以最高位不表示正负,而int类型的最高位是表示正负的,1表示负数,0表示正数。(计算机组成原理中的知识)

② 计算机中负数的存储方式——其补码。

在计算机中,负数存储的方式都是以其其补码形式储存,例如:6在计算机的存储形式是0000 0000 0000 0110(32位计算机中),而-6是1000 0000 0000 0110 (32位机下)(最高位是符号位),补码也就是1111 1111 1111 1010(32位计算机中),补码=原码取反+1。

③ 赋值一个负数给unsigned int类型的变量,它的意思就是将-6这个数的补码形式(1111 1111 1111 1010(32位机下))形式存到一个叫b的空间中,下次我读取这个数的时候,是使用b这个名字,按照b这个名字的类型来读取的,所以当b是unsigned int类型的时候,就不考虑最高位是符号位,则b = 1111 1111 1111 1010(32位机下)(因为是无符号),当b是int类型的时候,考虑最高位是符号位(1为负数,0为正数),当发现是1的时候也就是负数的时候,计算机会把它转换成为原码从而读出是-6。所以一个变量的类型是决定读取这个变量存储的数的方式。

④ 隐式转换。

当unsigned int类型与int类型运算时,如果没有指出转换的类型,它会进行“隐式转换”转换为unsigned int类型,也可以记住,不同类型进行运算时,没指出转换的类型时,它会往容量大的那总类型转换,因为这样可以更加容易避免溢出嘛。

代码分析:

首先,定义了一个 int a = 3 。

a的存储以3的二进制形式存储。 unsigned int b = -6 。
b的存储是以其补码存储的,也就是1111 1111 1111 1010(32位机下)。

当执行a+b的时候,printf函数要求指定打印出%d,所以,a以int(%d)类型读取出来,也就是3;b也是以int(%d)类型读取出来,也就是-6,所以3+(-6)=-3,发现打印结果是-3。

而在后面的 if((a+b)>0) 判断中,(a+b)中并没有指定读取的类型,所以就进行隐式转换,a就转成unsigned int类型,也就是3;b转成unsigned int类型,因为b本来的存储是1111 1111 1111 1010(32位机下),转成unsigned int类型时不考虑符号位,所以就直接以补码的形式转化,也就是65530(32位机下),所以a+b=65533>0,输出yes。

问题总结

① 当一个int类型的a与unsigned int类型的b相运算时,例如:a+b的时候,如果直接是a+b的话,各个变量就会隐式转换成为unsigned int的类型再相加。

② 当用print函数指定打印的时候,例如:printf(”%d\\n”,a+b),就要各个变量转换为%d的类型再想加。

③ 当用一个int c类型去装这个结果时,如:c=a+b,同理,a和b会根据c的类型去分别转换跟c一致的类型再去运算。

例:1:

如下程序:

#include<stdio.h>
#include<iostream>

using namespace std;

int main()
{
	int a = -1;
	unsigned int b = 16;
	int c = -17;
	printf("%u\\n", a + b);
	printf("%u\\n", c + b);	//表示为unsigned int输出
	printf("%d\\n", c + b);	//%d表示为int型输出

	return 0;
}

输出结果为:

其实运算a+b或者c+b时都是现将其转化成unsigned int型再进行运算的,
a的补码为0xFFFFFFFF,当将其转化为无符号数时为4294967295
b的补码为0x00000010,两个相加会溢出,结果为0x0000000F,即15
c的补码为0xFFFFFFEF,转化为无符号数为4294967279,c和b相加为4294967295。它是-1的补码

其实我们可以肉眼先求出-17+16=-1,然后求出-1的unsigned 值

例2:

如下程序:

#include<stdio.h>
#include<iostream>

using namespace std;
/*
当表达式中存在符号类型和无符号类型时
所有的操作数都自动转换为无符号类型
*/
char getChar(int x, int y)
{
	char c;
	unsigned int a = x;
	unsigned int b = a + y;
	(a + y > 10) ? (c = 1) : (c = 2);
	return c;
}

int main() {
	char c1 = getChar(7, 4);
	char c2 = getChar(7, 3);
	char c3 = getChar(7, -7);
	char c4 = getChar(7, -8);

	printf("c1 = %d\\n", c1);
	printf("c2 = %d\\n", c2);
	printf("c3 = %d\\n", c3);
	printf("c4 = %d\\n", c4);

	return 0;
}

输出结果为:

注意:(-7)+7=0,其unsigned 的值还是0
但是 (-8)+7=-1,其unsigned的值远远大于0

以上是关于有符号数与无符号数之间运算问题探究的主要内容,如果未能解决你的问题,请参考以下文章

无符号数和有符号数的转化

02_有符号数与无符号数

有符号数的加减法 和无符号数的加减法,和,系统是如何识别有符号数和无符号数的

有符号数与无符号数比较的坑

有符号与无符号数之间赋值的截断和扩展

verilog中有符号数的运算