在单片机编程中,我们经常会用到一些无符号数与有符号数的混合运算,另外我们所用的单片机很有可能是16位或者8位的,这样,编程时所用的一些变量的取值范围会对我们的 运算有所限制.比如说8位的单片机无符号数最大值为255,有符号最大数为127;16位单片机无符号数最大值为65535,有符号数最大值为32767.对于32的单片机来说,因为我们一般所处理的值很少能超过有符号数的最大取值,所以比较少遇到下面出现的问题.

        在一些运算中,我们希望有些数能表示正负,这就得用有符号数,而有些数的取值会超过有符号数的最大值,这时我们就得用无符数来表示.下面是我编程时遇到的两个问题(用的是MC9S12XS128处理器,16位的单片机).

       变量的声明如下:

        int iError;

        unsigned  int uiExpectSpeed;

        unsigned int uiCurrentSpeed;

       语句如下:

       iError  = (uiExpectSpeed - uiCurrentSpeed)/3;       //(1) 第一个语句

       在调试的过程中发现这个iError的值有时候会特别大,最后才发现是上面的这句语句出错了!然后修改成下面两句结果就对了:

       iError  = uiExpectSpeed - uiCurrentSpeed;            //(2)第二个语句

       iError  = iError/3;                                                          //(3)第三个语句

       不同类型的数据在进行混合运算时会有一个隐试的类型转换过程,有符号数与无符号数混合运算,有符号数会被转换成无符号数后再参加运算.

       在上面的第一个语句中,如果uiExpectSpeed 比uiCurrentSpeed的值大,也就是uiExpectSpeed - uiCurrentSpeed结果为一正值,那不会出现啥问题,但当uiExpectSpeed 比uiCurrentSpeed的值小时就出现问题了,此时uiExpectSpeed - uiCurrentSpeed的临时结果存放在16位的寄存器中,且最高位1,对于有符号数来说会把这一个位解释为符号位,1表示负数,而对于无符号来说这个位就表示数值,接着这个临时的结果除以3后,所得到的结果的最高位变为了0此时该结果会转换为一个有符合数(不管是有符号数,还是无符号数,最高位为0时,所表示的数值就是一样的),赋给iError.本应该得到一个负数的,但最终却得到了一个比较大的正数!在第一个语句中,如果没有除以3,而是两个数作差后直接赋给iError则是不会出错的,虽然uiExpectSpeed - uiCurrentSpeed运算的结果是一个很大的正数(寄存器的最高位为1),但在这个临时结果赋给iError这个变量时,会先把这个值转换为一个有符号数赋给iError.其实,在把uiExpectSpeed - uiCurrentSpeed运算的结果赋给iError时是把所有的位原封不动的复制到iErrorr所表示的内存单元中的,只是我们是以有符号数来解释这个内存单元中的内容,所以这个很大的正数就变成了一个负数!(数据在处理器内是以补码表示的,对于数据是正还是负只是人们的解释不同而已).所以我就用后面的两句替换了第一句,这样不管uiExpectSpeed - uiCurrentSpeed的差值是正还是负都能得到正确的结果了.

   

      下面是我在做超声波测距时遇到的又一个很隐蔽的问题:

       unsigned int start; //表示计时开始时计数器的值
       unsigned int end; //表示计时结束时计数器的值
       unsigned int error;      

      unsigned int distance; //表示距离
      unsigned int time; //表示从计时开始到结束所用的时间
      unsigned int remainder;//余数

      start  = TCNT;// 计时开始, TCNT为16位的计时器寄存器

     ..............一段时间后(这段时间小于计时器TCNT从0计数到最大值65535所表示的时间)...........

     end = TCNT; //计时结束

     error = end - start; //注意,end有可能比start小,但由于都是无符号数,所以最后得到的差值就是这段时间内计数器TCNT的增量.

      time = error/625; //单位为ms  TCNT每1ms内数值增加625(这个数与TCNT所用的时钟有关)
      distance = 17*time; //单位为cm, 距离为速度乘以时间再除以2就是声波所传波的距离

      这块由于是分步计算的,所以会有比较大的误差(主要是由于error/625后的余数被丢弃了)     于是我改成如下语句:

 

     start  = TCNT;// 计时开始, TCNT为16位的计时器寄存器

     ..............一段时间后(这段时间小于计时器TCNT从0计数到最大值65535所表示的时间)...........

     end = TCNT; //计时结束

     error = end - start; //注意,end有可能比start小,但由于都是无符号数,所以最后得到的差值就是这段时间内计数器TCNT的增量.

     distance = (17*error)/625; //单位为cm, 将上面的最后两句结合成一句,先乘后除就会减小误差

     但改后上面distance = (17*error)/625; 这句就错了,因为error的值可能很大,最大可以达到65535,所以17*error结果很有可能会超过65535,但这个处理器是16位的,也就是说这个处理器的数据寄存器为16位,最大的表示数值也就65535,所以17*error大于65535后就会被截断存入寄存器中.也就是说存入寄存器中的值为(17*error)e536,当再用这个值除以625时得到的很有可能就是0或者个位数的值,不管怎样,此时得到的结果都是错误的值了!!

     结合上面两种情况,最后我改成如下:   

      start  = TCNT;// 计时开始, TCNT为16位的计时器寄存器

     ..............一段时间后(这段时间小于计时器TCNT从0计数到最大值65535所表示的时间)...........

     end = TCNT; //计时结束

     error = end - start; //注意,end有可能比start小,但由于都是无符号数,所以最后得到的差值就是这段时间内计数器TCNT的增量.

     time = error/625; //单位为ms
     remainder = error - time*625;//计算上一句中丢弃的余数,没有用remainder = errorb5,是因为除法很耗时!!
     distance = 17*time + (17*remainder + 312)/625; //单位为cm,此处的312(625/2)是考虑到四舍五入的.

来自:http://blog.csdn.net/dlutxie/article/details/6709510

单片机中无符号数运算出现的问题相关推荐

  1. 51单片机的有符号数和无符号数运算

    内容:讲解有符号数运算和无符号数运算的区别 在51单片机中,所有的实数都是以补码的形式存在于内存中的.而各种算术运算也都是以补码的方式进行的.至于算出来的结果是正是负,完全在于你如何去看这个数.这里举 ...

  2. Verilog 有符号数与无符号数运算

    无符号数运算,左值位宽不够,发生截断的现象 reg [3:0] a = 4'b1111;//15 reg [3:0] b = 4'b0010;//2 wire [3:0] c; wire [3:0] ...

  3. java中有符号数和无符号数,C语言中无符号数和有符号数之间的运算

    C语言中有符号数和无符号数进行运算(包括逻辑运算和算术运算)默认会将有符号数看成无符号数进行运算,其中算术运算默认返回无符号数,逻辑运算当然是返回0或1了. unsigned int和int进行运算 ...

  4. C语言中无符号数和有符号数之间的比较和运算

    学C语言的同学看看以下代码运行结果会多少 源代码 #include <stdio.h> void main(void) {unsigned char uchar_num = 1;char ...

  5. C++中无符号数与有符号数的转换

    C++中的无符号数与有符号数的转换 1.无符号数转为有符号数 unsigned short int a = 50000;signed short int b = 0;b = a;cout <&l ...

  6. C语言无符号数运算问题

    C语言中有符号数和无符号数进行运算(包括逻辑运算和算术运算)默认会将有符号数看成无符号数进行运算,其中算术运算默认返回无符号数,逻辑运算当然是返回0或1了. unsigned int和int进行运算 ...

  7. 无符号数运算相减/有符号数表示运算问题(一)

    参考链接:https://blog.csdn.net/qq_38608897/article/details/103966054?utm_medium=distribute.pc_relevant.n ...

  8. C语言中无符号数和有符号数相加问题

    看个题: #include<stdio.h> int main() { unsigned int a=6; int b=-20; printf("%d\n",a+b); ...

  9. 单片机中的移位运算##

    左移运算符"<<":按照二进制形式把所有的数字向左移动对应的位移位数,(高位移出,低位补零). 右移运算符">>":按照二进制形式把所有 ...

最新文章

  1. bzoj 3209 数位DP+欧拉定理
  2. Apache Fluo:填充Google搜索索引的Percolator的实现
  3. Django REST framework【学习内容】
  4. 计算机网络课设不会,计算机网络课设讲述.doc
  5. redmine一键安装
  6. 微课|玩转Python轻松过二级(3.4节):集合操作与应用
  7. 【ArcGIS|空间分析】3D可视性分析(全国大学生GIS技能大赛试题)
  8. PDG格式转换PDF格式的方法
  9. 计算机除氧化的方法,内存条氧化了的解决方法
  10. shell输出毫秒_Shell获取毫秒时间
  11. 数据结构实习——重言式的判别(写的不好不要见怪)
  12. 南京计算机工程大学分数线,2017南京信息工程大学录取分数线
  13. 手机访问电脑的静态文件(html...)(anywhere)
  14. 论“期权股”的财富经 !!!
  15. CISCO和华为交换机修改密码
  16. 最新流浪猫流浪狗H5完整运营源码下载/可封装APP
  17. Unity 2D游戏制作
  18. 采用ImageJ+插件批量转换透射电镜ser图像
  19. 基于python3的社区发现模块度计算
  20. 离“失业”还有多远?机器人流程自动化是怎样改变人类生活的?

热门文章

  1. chrome设置允许跨域
  2. 坐躺都无敌舒服,对老腰非常友好的保友金豪ew2代人体工学椅
  3. [29期] 随便说说我的学习感受
  4. eclipse 快捷键保存在哪里
  5. java根据一个 号截取_java截取最后一个号前的字符串
  6. 使用码云制作简易图床的方法
  7. 从app跳转到手机设置(代码)
  8. 42 位专家,12 场演讲,龙蜥社区系统安全 MeetUp 精彩回顾来啦
  9. 简述连接因特网的方式和静态IP、PPPOE、DHCP
  10. 侯捷C++个人学习笔记面向对象Part2