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

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

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


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


这个问题测试是否懂得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

相关文章
|
6月前
|
存储
【机组期末速成】计算机的运算方法|进制转换|无符号数与有符号数|数的定点表示与浮点表示|定点运算
【机组期末速成】计算机的运算方法|进制转换|无符号数与有符号数|数的定点表示与浮点表示|定点运算
177 0
|
11天前
|
存储 编译器
二进制相关概念、运算与应用
本文详细介绍了二进制、原码、反码和补码的概念及其应用,包括8位二进制的表示范围和常见位运算符的使用方法。通过具体示例,解释了位运算在数值翻倍、字符大小写转换和IP地址生成中的巧妙运用。此外,还提供了进制间转换的方法和Java-API中的支持。本文旨在帮助读者深入理解位运算的基础和实际应用。
28 1
二进制相关概念、运算与应用
|
1月前
|
Java 开发者
【编程基础知识】2的n次幂与二进制位全为1之间的联系,为啥只差一个1
本文深入探讨了2的n次幂与二进制位全为1之间的数学联系,解释了2的n次幂减一的二进制表示为何全为1,并探讨了这一特性在HashMap中的应用。通过基础数学原理和实际代码示例,文章揭示了这一特性的实用价值,适合各水平的编程爱好者学习。
18 3
|
6月前
补码浮点数运算(设数的阶码为3位,尾数为6位(均不包括符号位)按机械补码浮点运算规则完成下列[x+y]补运算。)
补码浮点数运算(设数的阶码为3位,尾数为6位(均不包括符号位)按机械补码浮点运算规则完成下列[x+y]补运算。)
342 0
|
11月前
|
存储
【408计算机组成原理】—加减运算和溢出判断(八)
【408计算机组成原理】—加减运算和溢出判断(八)
|
存储 人工智能 编译器
C语言之(有关%d和%u的有关内容,输出方法)(有符号和无符号在内存中的存储情况)(整形无符号数和有符号数是如何进行计算的,整形无符号数和有符号数在循环中的应用举例)
C语言之(有关%d和%u的有关内容,输出方法)(有符号和无符号在内存中的存储情况)(整形无符号数和有符号数是如何进行计算的,整形无符号数和有符号数在循环中的应用举例)
471 0
计算机组成原理——浮点数加减运算&强制类型转换
计算机组成原理——浮点数加减运算&强制类型转换
822 0
计算机组成原理——浮点数加减运算&强制类型转换
进制之间的快速转换法:8421码及原码反码补码之间的相互转换关系
进制之间的快速转换法:8421码及原码反码补码之间的相互转换关系
496 0
|
人工智能 算法 C++
【基础算法】关于高精度计算的问题【很高位数数据的加减乘除(相关代码用C++实现)】
【基础算法】关于高精度计算的问题【很高位数数据的加减乘除(相关代码用C++实现)】
二进制浮点数的加减法运算
二进制浮点数的加减法运算