算术类型转化_无符号数 和 带符号数

算术类型转化

1. 无符号数 和 带符号数

这里按照 32位 进行讨论

无符号数 和 带符号数 进行运算时,带符号数会转换成无符号数,运算结果是 无符号类型

见 《C++ Primer - 5th》P34

unsigned int u = 10;

int i = -42, j = -5;

auto res1 = i + u;

cout << "res1 = " << res1 << "\t type = " << typeid(res1).name() << endl;

// res1 = 4294967264        type = unsigned int

auto res2 = j + u;
cout << "res2 = " << res2 << "\t type = " << typeid(res2).name() << endl;

//res2 = 5         type = unsigned int

1.1 原因


数字在计算机中的表示,原码 反码 补码

1 = [0000 0001] (原码) = [0000 0001] (反码) = [0000 0001] (补码)

正数,原码 反码 补码 都是一样的


-1 = [1000 0001] (原码) = [1111 1110] (反码) = [1111 1111] (补码)

负数

反码:原码的基础上,符号位保持不变,其余各位取反

补码:原码的基础上,符号位保持不变,其余各位取反,末尾+1


int i= -42;
// 原码:1000 0000 0000 0000 0000 0000 0010 1010
// 反码:1111 1111 1111 1111 1111 1111 1101 0101 
// 补码:1111 1111 1111 1111 1111 1111 1101 0110  末尾 +1

unsigned int u = 10;
// 补码:0000 0000 0000 0000 0000 0000 0000 1010
// 原码=反码=补码

i + u
//i = 42 的补码:1111 1111 1111 1111 1111 1111 1101 0110
//u = 10 的补码:0000 0000 0000 0000 0000 0000 0000 1010
//i + u  的补码:1111 1111 1111 1111 1111 1111 1110 0000
//此时 i + u 的结果按照 无符号数 来理解
//即 1111 1111 1111 1111 1111 1111 1110 0000 按照 无符号数 来解释

1111   1111   1111   1111   1111   1111   1110   0000 + 1   1111 = 2 32 − 1 1111\ 1111\ 1111\ 1111\ 1111\ 1111\ 1110\ 0000 + 1\ 1111 = 2^{32}-1 1111 1111 1111 1111 1111 1111 1110 0000+1 1111=2321

按照 无符号数 来解释

1111   1111   1111   1111   1111   1111   1110   0000 = 2 32 − 1 − 31 = 4294967264 1111\ 1111\ 1111\ 1111\ 1111\ 1111\ 1110\ 0000 = 2^{32}-1-31 = 4294967264 1111 1111 1111 1111 1111 1111 1110 0000=232131=4294967264

但是如果按照 int 来解释

1111   1111   1111   1111   1111   1111   1110   0000 1111\ 1111\ 1111\ 1111\ 1111\ 1111\ 1110\ 0000 1111 1111 1111 1111 1111 1111 1110 0000 是某个 int 的补码,则反求它的原码,就能知道它真正的值

还原一下

1111   1111   1111   1111   1111   1111   1101   1111 1111\ 1111\ 1111\ 1111\ 1111\ 1111\ 1101\ 1111 1111 1111 1111 1111 1111 1111 1101 1111 【反码】

1000   0000   0000   0000   0000   0000   0010   0000 1000\ 0000\ 0000\ 0000\ 0000\ 0000\ 0010\ 0000 1000 0000 0000 0000 0000 0000 0010 0000 【原码】

表示 -32

综上,整理一下

int i = - 42 补码表示为 1111   1111   1111   1111   1111   1111   1101   0110 1111\ 1111\ 1111\ 1111\ 1111\ 1111\ 1101\ 0110 1111 1111 1111 1111 1111 1111 1101 0110

… u = 10 补码表示为 0000   0000   0000   0000   0000   0000   0000   1010 0000\ 0000\ 0000\ 0000\ 0000\ 0000\ 0000\ 1010 0000 0000 0000 0000 0000 0000 0000 1010

计算结果 补码表示为 1111   1111   1111   1111   1111   1111   1110   0000 1111\ 1111\ 1111\ 1111\ 1111\ 1111\ 1110\ 0000 1111 1111 1111 1111 1111 1111 1110 0000

按照 无符号 解释为 4294967264

按照 带符号 解释为 -32

其中 4294967264 + 32 = 2 32 4294967264+32=2^{32} 4294967264+32=232

首先,计算机使用 补码 是为了便于负数的运算,因为 负数 + 正数 如果按照原码计算,需要每一位减过去,可能还要借位,十分麻烦;使用 补码 就可以的话,负数 + 正数 就可以像 正数 + 正数 那样 直接每一位 相加,当然结果还是以 补码 的形式表示,最后还要转换到 原码 我们才能知道结果是几

  • 上述 int + unsigned int 相加的过程虽然发生类型转换,但是底层的二进制数据其实没有变化

  • int 转换成 unsigned int 底层二进制数据没有变化,但是解释方式变了,现在按照 unsigned int 去解释

  • 尽管 int 变成了 unsigned int, 但是 相加的过程都是一样的,结果也是一样的

  • 由于 int + unsigned int 变成了 unsigned int + unsigned int, 结果也按照 unsigned int 来解释

所以,如果把 最后的结果按照 int 去解释,还是可以得到正确的结果

unsigned int u = 10;

int i = -42;

auto res1 = i + u;

cout << "res1 = " << res1 << "\t type = " << typeid(res1).name() << endl;

// res1 = 4294967264        type = unsigned int

cout << "按照 int 解释 :" << int(res1) << endl;
// 按照 int 解释 :-32

再来看另一个结果

int j= -5;
// 原码:1000 0000 0000 0000 0000 0000 0000 0101
// 反码:1111 1111 1111 1111 1111 1111 1111 1010 
// 补码:1111 1111 1111 1111 1111 1111 1111 1011  末尾 +1

unsigned int u = 10;
// 补码:0000 0000 0000 0000 0000 0000 0000 1010
// 原码=反码=补码

j + u
//j = -5 的补码:1111 1111 1111 1111 1111 1111 1111 1011
//u = 10 的补码:0000 0000 0000 0000 0000 0000 0000 1010
//j + u  的补码:0000 0000 0000 0000 0000 0000 0000 0101
//此时 j + u 的结果按照 无符号数 来理解
//即 0000 0000 0000 0000 0000 0000 0000 0101 按照 无符号数 来解释

按照 无符号数 和 带符号数 解释 ,结果都是 5

1.2 简单理解记忆

为了便于理解和记忆,可以这么说

  • -1 的补码表示 1111   1111   1111   1111   1111   1111   1111   1111 = 2 32 − 1 1111\ 1111\ 1111\ 1111\ 1111\ 1111\ 1111\ 1111 = 2^{32}-1 1111 1111 1111 1111 1111 1111 1111 1111=2321

  • -42的补码表示 1111   1111   1111   1111   1111   1111   1101   0110 = 2 32 − 42 1111\ 1111\ 1111\ 1111\ 1111\ 1111\ 1101\ 0110 = 2^{32}-42 1111 1111 1111 1111 1111 1111 1101 0110=23242

如果能给 -42 加上 41(10 1001),就可以得到 -1;

而如果加上42(10 1010),就可以得到 0,( 2 32 2^{32} 232 溢出,变成 0,这里只有32位)

此处是 -42 + 10, 很遗憾,-42 在向 -1 靠拢,但是不够,停留在中间,它的底层表示是一个前面很多 1 (首位也是1)的 二进制数,由于类型转换,按照 无符号类型 去解释,被解释成一个很大的数。


  • 故 -42(int) + 10(unsigned int)

从底层补码的角度,按照无符号去解释

= 2 32 − 42 + 10 = 4294967264 2^{32}-42 +10 =4294967264 23242+10=4294967264


  • 而 -5(int) + 10(unsigned int)

从底层补码的角度,按照无符号去解释

= 2 32 − 5 + 10 = 2 32 + 5 = 5 2^{32}-5 +10 =2^{32}+5 =5 2325+10=232+5=5


1.3 可能犯错的代码

一般 容器的 size() 的返回类型是一个 无符号类型 的数,可以表示一个极大的数,参考《C++ Primer 5-th》P103

vector<int> v;

int len =-1 ;

if (v.size() < len)
    cout << "v.size() <<小于<< " << len << endl<< endl;
else
    cout << "v.size() >>大于>> " << len << endl<<endl;

// v.size() <<小于<< -1

while (v.size() < len)
{
    v.push_back(1);
    cout << "push_back()" << endl;

    if (v.size() == 10)  // 去掉 break,就会变成死循环
        break;
}

/*
    push_back()
    push_back()
    push_back()
    push_back()
    push_back()
    push_back()
    push_back()
    push_back()
    push_back()
    push_back()
*/

cout << v.size() << endl;  // 10

上述代码中,v.size()len 发生运算, 即 无符号带符号 发生 运算

那么 int 型 的 len 就会转换成 无符号 类型,注意转换不会改变底层的二进制数据(补码表示),只是解释 它 的方式变了,原来按照 int 解释是-1,现在按照 无符号 来解释,而 -1 的 补码 是全 1 的二进制,按照 无符号 解释就是一个 极大 的数;

所以 在 上述代码 的 ifwhile 的判断语句中,len 被解释成一个极大的数字,则条件为真,输出

v.size() < -1 ,同时,

while() 被不断执行(去掉 break, 就是死循环)


如果要对上面的代码进行改正,可以将 v.size() 的返回值 转换成 int, 即 int( v.size() )

vector<int> v;

int len = -1;

if (int(v.size()) < len)   // if 条件不成立
    cout << "v.size() <<小于<< " << len << endl << endl;
else
    cout << "v.size() >>大于>> " << len << endl << endl;

// v.size() >>大于>> -1

while (int(v.size()) < len)  // while 条件不成立
{
    v.push_back(1);
    cout << "push_back()" << endl;

    if (v.size() == 10)
        break;
}

cout << v.size() << endl;  // 0

附上 leetcode 一道题,方法 2 ( while ((int)bits.size() <= len) )

902. 最大为 N 的数字组合

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值