惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The Cloudflare Blog
小众软件
小众软件
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
T
Tailwind CSS Blog
WordPress大学
WordPress大学
有赞技术团队
有赞技术团队
博客园 - 司徒正美
V
Visual Studio Blog
G
Google Developers Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
月光博客
月光博客
aimingoo的专栏
aimingoo的专栏
博客园_首页
Blog — PlanetScale
Blog — PlanetScale
博客园 - 聂微东
S
SegmentFault 最新的问题
T
The Blog of Author Tim Ferriss
D
Docker
Vercel News
Vercel News
Recent Announcements
Recent Announcements
Last Week in AI
Last Week in AI
爱范儿
爱范儿
J
Java Code Geeks
大猫的无限游戏
大猫的无限游戏

浮华生

Elasticsearch 检索性能优化 - 浮华生 舆情监控系统综述 - 浮华生 2024 半年度总结 - 浮华生 2023 年终总结 - 浮华生 异地机器组网方案 - 浮华生 Kubernetes 部署 Elasticsearch 和 Kibana - 浮华生 2022 年终总结 - 浮华生 RabbitMQ connection channel 的关系 - 浮华生 Kafka Java 客户端 Producer 原理分析 - 浮华生 RabbitMQ 和 Kafka 应用原理简单对比 - 浮华生 阿里云 OpenSearch 介绍 - 浮华生 Golang Array 和 Slice 区别 - 浮华生 Mac OS 下打造 golang nvim 编程环境之基础配置 - 浮华生 电商搜索技术总结 - 浮华生 电商搜索业务总结 - 浮华生 2021 年终总结 - 浮华生 Cypress 实践总结 - 浮华生 年终总结 - 浮华生 关于我 - 浮华生 使用 cucumber 进行行为驱动开发(BDD) - 浮华生 微服务应用集成 SpringCloud 步骤 - 浮华生 电商搜索数据同步方案 - 浮华生 macOS 系统推荐的一些软件 - 浮华生 DevOps 实施规划(持续更新) - 浮华生 rabbitmq 如何提高可靠性并保证消费端幂等 - 浮华生 AMQ Model总结 - 浮华生 结对编程 - 浮华生 RSocket 介绍 - 浮华生 面向对象的理解 - 浮华生 企业平台技术框架 - 浮华生
通过一道数值转换题重温计算机补码 - 浮华生
浮华生 · 2020-07-02 · via 浮华生

问题

昨天同事和我聊了他偶然间看到的一道面试题,引出了后面的深入分析。

1
System.out.println(Integer.valueOf((char)-1)); //output: 65535

为什么输出是 65535 而不是 -1 呢?

分析

先回顾一下计算机相关的知识点:

原码(True form)

原码是指一个二进制数左边加上符号位后所得到的码,且当二进制数大于0时,符号位为0;二进制数小于0时,符号位为1;二进制数等于0时,符号位可以为0或1(+0/-0)。 –维基百科

原码根据符号位能很直观的看出是正数还是负数,但是对于计算机运算来说却不是那么好用了,比如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 求解: 1 + (-1) 
---
站在人类的视角会立马得出 1 + (-1) = 0 的结论
---
而计算机的角度(假设长度是8-bit):
转换成二进制并相加:
  00000001
+ 10000001
 ----------
  10000010
 
这个结果是:-2,与正确值不符

所以如果有负数使用了原码运算,那还得把符号位撇开,这就增加了使用成本,为了解决这个问题就出现了反码。


反码(ones' complement),也叫一的补码

二进制数每个数字反转,得到的数即为原二进制的一的补码(英语:ones' complement)。若某一位为0,则使其变为1,反之亦然。

  • 一的补码以有符号比特的二进制数定义。
  • 一的补码是有符号比特的二进制数。
  • 正数和0的一的补码就是该数字本身。–维基百科

反码能解决负数运算的问题吗,可以,但是又会出现负零循环进位的问题。举例:

1
2
3
4
5
6
7
8
9
# 求解  1 + (-1)
---
转换成二进制并取反相加
  00000001 (正数的反码是它本身)
+ 11111110 (符号位不变其余取反)
------------
  11111111 
转换成原码为:100000000 = -0
  

对于 0 来说正负并没有意义, 而且还会有反码 0000 0000 和 1111 1111 这两种 0 的表现形式,在使用反码运算时判断是否为 0 还需要考虑这两种情况,为了解决这个问题就又出现了补码。


补码(2’s complement)

正数和0的补码就是该数字本身。负数的补码则是将其对应正数按位取反再加1。

补码系统的最大优点是可以在加法减法处理中,不需因为数字的正负而使用不同的计算方式。只要一种加法电路就可以处理各种有号数加法,而且减法可以用一个数加上另一个数的补码来表示,因此只要有加法电路及补码电路即可完成各种有号数加法及减法,在电路设计上相当方便。

另外,补码系统的0就只有一个表示方式,这和反码系统不同(在反码系统中,0有二种表示方式),因此在判断数字是否为0时,只要比较一次即可 –维基百科

补码有一个原则:数字a的补码为 -a,举例:

1
2
3
4
# a = -5,那么 a 的补码为 5
10000101  # -5 的原码
11111010  # -5 的反码
11111011  # -5 的补码 

解答

上面的问题可以拆分成下面的代码便于分析

image-20200707094407456

int(32-bit) 转 char(16-bit) 会失去精度

1
2
3
4
1000 0000 0000 0000 0000 0000 0000 0001   #int -1 的原码
1111 1111 1111 1111 1111 1111 1111 1111   #int -1 的补码(内存中的数值用补码表示 0Xffffffff) 
                    1111 1111 1111 1111   #char 只保留 16 位;10进制为 65535
0000 0000 0000 0000 1111 1111 1111 1111   #再转换成 int 执行零扩展;10进制同样为 65535             

扩展

溢出

符号扩展与零扩展

循环进位