C++-两个无符号数相减的问题


因为一个裸机软定时器的溢出问题,

static uint32_t _timer_ticks = 0;

void timer_ticks()
{
    _timer_ticks++;
}

void soft_timer_loop()
{
    struct soft_timer* target;
    for(target = head_handler; target; target = target->next)
    {
        if(_timer_ticks >= target->timeout) //这里有机会溢出 是个bug
        {
            if(target->repeat == 0)
            {
                soft_timer_stop(target);
            } 
            else 
            {
                target->timeout = _timer_ticks + target->repeat; 
            }
            target->timeout_cb();
        }
    }
}

为了解决这个bug,有人可以把判断条件换成

if((int)((uint32_t)(target->timeout -_timer_ticks)) <= 0) 
{
    
}

这个涉及到2个无符号数相减的问题。
微机原理中计算通过ALU计算,加减的时候,ALU不认识什么符号。他有两个输入端A,B。 A端直接输入,B端的看情况,如果是+则直接输入,如果是-则经过一个反向器并加1输入。 他只管计算,并把结果反回给程序,至于这个结果是什么类型,就由程序来处理了

简单一句,无符号数的加减法等同于补码进行加减法,只是最后对于结果的解释不一样,无符号数A-B,可以和补码表示的有符号数一样,转化成,A+(B的补码),即A+(~B+1 )

这里假设_timer_ticks和 target->timeout都是8位的。target->repeat +_timer_ticks 溢出了。1ms计数的情况下,按实际 target->timeout是要比4294967295(49.7天)要小的多的一个数。
这里按8位数写
假设_timer_ticks = 250 ,target->repeat = 10 则 target->timeout = 4 
则target->timeout -_timer_ticks  
_timer_ticks 原码 1111 1010
                    补码 0000 0110
target->timeout + 0000 0110 = 10强制转换为int 也是大于0的。
_timer_ticks = 251 ((uint8_t)(target->timeout -_timer_ticks)) = 0x09转为有符号 (char)((uint8_t)(target->timeout -_timer_ticks)) = 9

。。。
_timer_ticks = 255 
((uint8_t)(target->timeout -_timer_ticks)) =0x05转为有符号(char)((uint8_t)(target->timeout -_timer_ticks)) = 5
。。。
_timer_ticks = 3 
 ((uint8_t)(target->timeout -_timer_ticks)) = 0x01转为有符号(char)((uint8_t)(target->timeout -_timer_ticks)) = 1

_timer_ticks = 4 
 ((uint8_t)(target->timeout -_timer_ticks)) = 0x00转为有符号(char)((uint8_t)(target->timeout -_timer_ticks)) = 0

_timer_ticks = 5  ((uint8_t)(target->timeout -_timer_ticks)) = 0xff转为有符号(char)((uint8_t)(target->timeout -_timer_ticks)) < -1

所以解决了溢出的的时候,因为target->timeout 必定比_timer_ticks小,而立即执行的BUG。


那么这么些有bug吗?首先没有溢出的情况下,很好理解,这时候其实不用考虑什么补码,不用再绕一层,按常识走。
就是溢出的情况下,有,假设在_timer_ticks = 128的时候  target->repeat = 128 ,这是target->timeout = 1
_timer_ticks = 129  (int)((uint32_t)(target->timeout -_timer_ticks)) = -128 这tm就成立了啊。

或者说_timer_ticks = 128的时候  target->repeat = 129 这是target->timeout = 2
_timer_ticks = 129  (int)((uint32_t)(target->timeout -_timer_ticks)) = -129 这tm就成立了啊。
_timer_ticks = 130  (int)((uint32_t)(target->timeout -_timer_ticks)) = -128 这tm就成立了啊。

 

这里再极限一些 target->repeat = 250,当_timer_ticks = 6的时候,target->timeout = 1
_timer_ticks = 2  (int)((uint32_t)(target->timeout -_timer_ticks)) = -1 这tm就成立了啊。

这里只要target->repeat <= 127(UINT8_MAX/2) 就不会出现这种bug,而uint32的一半要20多天,所以这个bug,注意一下就行,平时不会出现。

还有一种方法,就是_timer_ticks target->timeout定义为64位的 那样溢出需要1亿年,根本不会溢出了。

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM