一 参考博文

java中无符号类型的解决方案

原码、反码、补码知识详细讲解(此作者是我找到的讲的最细最明白的一个)

0x80000000为什么等于-2147483648和负数如何在内存上储存

二 java中的无符号数和有符号数

在计算机中,可以区分正负的类型,称为有符号类型,无正负的类型,称为无符号类型。

  • 使用二进制中的最高位表示正负

    计算机中用补码表示数值;另外,用二进制的最高位表示符号,0表示正数、1表示负数。
  • 无符号和有符号数的范围的区别

    无符号数中,所有的位都用于直接表示该值的大小;有符号数中最高位用于表示正负,所以,正值时,该数的最大值就会变小:

    无符号数:1111 1111 值:255

    有符号数:0111 1111 值:127

    同样一个字节,无符号的最大值是255,有符号的最大值是127

三 java中的基本类型

Java的原始类型里除了char是无符号类型之外,其他都是有符号数据类型,如果需要某个宽度的无符号类型,可以用>>>进行转化,这个是java的无符号右移操作符,或者使用下一个宽度的带符号类型来模拟

例如,需无符号的short,就用int来模拟:

    int toUnsigned(short s) {
return s & 0x0FFFF;
}

java中十进制的字面常理只有一个特性,就是所有的十进制字面常量都是正数,如果想写一个负的十进制,则需要在正的十进制字面常量前面加上“-”就好了。

但是十六进制或者八进制的字面常量就不一定是正数或者负数,如果最高位是1,那么就是负数:

        System.out.println(0x80);//128
//0x81看作是int型,最高位(第32位)为0,所以是正数
System.out.println(0x81);//129
System.out.println(0x8001);//32769
System.out.println(0x70000001);//1879048193
//字面量0x80000001为int型,最高位(第32位)为1,所以是负数
System.out.println(0x80000001);//-2147483647
//字面量0x80000001L强制转为long型,最高位(第64位)为0,所以是正数
System.out.println(0x80000001L);//2147483649

四 补码与真值

这里先看一个问题:

    @Test
public void test01(){
System.out.println(0x80000000); // -2147483648
}

这个结果是怎么得来的?

要搞明白这个问题,得先明白几个概念:

  • 机器数:

    一个数在计算机中的二进制表示形式, 叫做这个数的机器数。机器数是带符号的,在计算机用一个数的最高位存放符号, 正数为0, 负数为1.

    比如,十进制中的数 3 ,计算机字长为8位,转换成二进制就是00000011。如果是 -3 ,就是 10000011 。那么,这里的 00000011 和 10000011 就是机器数
  • 真值:

    因为第一位是符号位,所以机器数的形式值就不等于真正的数值。例如上面的有符号数 10000011,其最高位1代表负,其真正数值是 -3 而不是形式值131(10000011转换成十进制等于131)。

    所以,为区别起见,将带符号位的机器数对应的真正数值称为机器数的真值(即补码表示的值)。

    例:0000 0001的真值 = +000 0001 = +1,1000 0001的真值 = –000 0001 = –1
  • 计算真值

    就拿-3来说,机器数为 10000011,那么补码是 11111101,所以真值就是补码的值:

    补码求值公式:补码的最高位有效位乘以(-1),然后按一般求二进制的方法求值

    例如:

    -3的补码 11111101 = (-1)12^7 + 12^6+.... 12^0 = -3

    3的补码 00000011 = (-1)027+........1*20= 3
  • 0x80000000问题解析

    再来看0x80000000为什么等于-2147483648,Java中用此十六进制表示int的最小值:
    /**
* A constant holding the minimum value an {@code int} can
* have, -2<sup>31</sup>.
*/
@Native public static final int MIN_VALUE = 0x80000000;

此十六进制数内存中存储的的确是0x80000001的二进制码。因为使用十六进制给int赋值时,这里的十六进制是补码形式。

也就是说,我们给变量赋的是补码,不是源码,所以会直接把0x80000001这个补码存入内存

补码求值得: 0x80000000 = (-1)1231+.....+0*20 = -2147483648

所以这个值是这样来的!

五 java中的数据类型符号扩展

先看一个jdk源码中int转为long用到的方法:

    @Test
public void test03(){
final long l = -5 & 0xffffffffL;
System.out.println(l); // 4294967291
}

如果运算一个操作数是long型,而另一个操作数是int类型。为了执行该计算,Java将int类型的数值用拓宽原生类型转换提升为long类型,然后对两个long类型数值相加。

因为int是有符号的整数类型,所以这个转换执行的是符号扩展。

-5 转换为long再转换为二进制,0xffffffff转换为二进制

进行与运算:

1111111111111111111111111111111111111111111111111111111110000101

0000000000000000000000000000000011111111111111111111111111111111

---------------------------------------------------------------------- & 与运算,两个都为1才为1,否则为0

0000000000000000000000000000000011111111111111111111111110000101= 4294967173 (十进制)

为什么-5转long前面要补1呢,这里就需要知道符号扩展规则:

窄的整型转换成较宽(字节数多)的整型时符号扩展规则:

如果最初的数值类型是有符号的,那么就执行符号扩展(即如果符号位为1,则扩展为1,如果为零,则扩展为0);

如果它是char,那么不管它将要被提升成什么类型,都执行零扩展,

如果将一个char数值c转型为一个宽度更宽的整型,并且希望有符号扩展,那么就先将char转型为一个short,它与char上个具有同样的宽度,但是它是有符号的

宽的整型转换成窄的整型直接截取低位的值,高位扔掉

所以上面-5符号是1,所以进行符号扩展前面都补1,补成long(64位),再进行位运算得出结果!

六 Java中byte转换int时与0xff进行与运算的原因

jdk源码中byte转int用到了 & 0xff,比如String的API:

    public static char charAt(byte[] value, int index) {
if (index < 0 || index >= value.length) {
throw new StringIndexOutOfBoundsException(index);
}
return (char)(value[index] & 0xff);// 先转int,再转char
}

这里为什么要用与运算呢? 因为char是无符号类型,所以不能进行符号扩展,需要零扩展,即前面补0

窄整型->宽整型要进行符号扩展,这里byte->cahr是窄到宽,如果不想进行符号扩展,则需要&0xff处理,先转int消除掉符号扩展,再转char即可

(b & 0xff)的结果是32位的int类型,前24被强制置0,后8位保持不变,然后转换成char型时,直接截取后16位。这样不管b是正数还是负数,转换成char时,都相当于是在左边补上8个0,即进行零扩展而不是符号扩展

至于为什么要进行零扩展: 因为char是无符号类型,他会把 1111 1111 当做65535而不是-1,,所以你前面补1的话数就会变很大,所以这里需要进行0扩展,于是 & 0xff这种骚操作就来了,这里确实有点绕!如果不看源码(并且要认真看啊,哈哈)一般发现不了这种问题

再比如下面代码:

    @Test
public void test01(){
byte b=-1;
System.out.println((int)b); // -1
System.out.println(b & 0xff); // 255
}

这里第二行255应该都好说,高位清零就是,至于直接强转为-1,那么符号扩展之后补码为11111111111111111111111111111111,求出结果原码:100000000000000000000000000001 还是-1,所以就是上面的结果,原理就是这样!

主要就是一个符号扩展延伸出来的问题!

java中符号类型和无符号类型的问题分析的更多相关文章

  1. C-基础:表达式中存在有符号类型和无符号类型时,都自动转换为无符号类型

    void foo(void) { unsigned ; ; (a+b > ) puts("> 6") : puts("<= 6"); } 答案 ...

  2. Java中返回值定义为int类型的 方法return 1返回的是int还是Integer&&finally中return问题

    在Java中返回值定义为int类型的 方法return 1:中返回的是Integer值,在返回的时候基本类型值1被封装为Integer类型. 定义一个Test类,在异常处理try中和finally中分 ...

  3. java中整数的默认为int类型的一些问题

    thingking in java 读书感悟 作者 :淮左白衣 写于2018年4月8日17:51:44 关于整数的默认类型,以及会产生的一些小问题 涉及基本数据类型的重载 关于整数的默认类型,以及会产 ...

  4. Java,C 位移运算符 有符号右移>>与无符号右移>>>

    个人博客 地址:https://www.wenhaofan.com/a/20181029232749 有符号右移 正数有符号右移 首先计算4>>2 将4转为二进制 0000 0100 右移 ...

  5. c++ 有符号int和无符号int做加减乘除问题

    c++ 有符号int和无符号int做加算术运算的问题: 一.运算过程先把有符号的补码数直接看成无符号数,在和无符号数进行算术运算 二.int和unsigned int类型进行混合算数运算时,运算结果为 ...

  6. C++切勿混用带符号类型和无符号类型

    如果表达式里既有带符号类型又有无符号类型,当带符号类型取值为负时会出现异常结果. 因为带符号数会自动转化为无符号数. 例如 a*b,a=-1, b=1,a是int,b是unsigned int,如果在 ...

  7. Java 中如何原样输出转义符号

    Java 中的转义字符有好几种,常见的有: 八进制转义字符,格式:\ + 1到3位八进制数字,如\1, \20,范围为 \0 ~ \377,即最大值为255. Unicode转义字符,格式:\u + ...

  8. Java中基本数据类型和包装器类型的关系

    在程序设计中经常用到一系列的数据类型,在Java中也一样包含八中数据类型,这八种数据类型又各自对应一种包装器类型.如下表: 基本类型 包装器类型 boolean Boolean char Charac ...

  9. java中如何使用BigDecimal使得Double类型保留两位有效数字

    一.场景:从数据表中读出Decimal类型的数据直接塞给Double类型的对象时,并不会有什么异常. 如果要再此基础上计算,就会发生异常. 比如:读出数据为0.0092,将其乘以100,则变成了0.9 ...

随机推荐

  1. bug(一)环境问题

    1.项目开发完成,部署到服务器进行测试,遇到一个问题: 同样的接口,同样的数据库表,同样的功能,得到不同的查询结果 具体如下图: 可以看到一个是有值的,一个是无值的,断点调试会发现所以的状态都是对的, ...

  2. ELK学习实验007:Nginx的日志分析系统之Metribeat配置

    一 Metricbeat 简介 1.1 系统级监控,更简洁将 Metricbeat 部署到您的所有 Linux.Windows 和 Mac 主机,并将它连接到 Elasticsearch 就大功告成了 ...

  3. .NET 半天搭建Jenkins持续集成与自动化部署系统

    前言 相信每一位程序员都经历过深夜加班上线的痛苦!而作为一个加班上线如家常便饭的码农,更是深感其痛.由于我们所做的系统业务复杂,系统庞大,设计到多个系统之间的合作,而核心系统更是采用分布式系统架构,由 ...

  4. win32汇编简单实现窗口程序

    .386 .model flat,stdcall option casemap:none ;========================== ;include部分 include windows. ...

  5. 洛谷$P2598\ [ZJOI2009]$狼和羊的故事 网络流

    正解:网络流 解题报告: 传送门! 昂显然考虑最小割鸭$QwQ$,就考虑说每个土地要么属于羊要么属于狼,然后如果一条边上是栅栏一定是相邻两边所属不同. 所以考虑给所有羊向$S$连$inf$,所有狼向$ ...

  6. $Noip2011/Luogu1315$ 观光公交 贪心

    $Luogu$ $Sol$ 觉得这题贪心要想很多事情,不适合我这种没脑子选手$ovo$.看题解还理解了很久. 最开始是这样想的:把所有的路段上的乘客按大小排个序用加速器就好了,这个想法被自己轻松$ha ...

  7. 使用ASP.NET Core 3.x 构建 RESTful API - 4.2 过滤和搜索

    向Web API传递参数 数据可以通过多种方式来传给API. Binding Source Attributes 会告诉 Model 的绑定引擎从哪里找到绑定源. 共有以下六种 Binding Sou ...

  8. Shiro权限管理框架(五):自定义Filter实现及其问题排查记录

    明确需求 在使用Shiro的时候,鉴权失败一般都是返回一个错误页或者登录页给前端,特别是后台系统,这种模式用的特别多.但是现在的项目越来越多的趋向于使用前后端分离的方式开发,这时候就需要响应Json数 ...

  9. 【一起学源码-微服务】Ribbon源码五:Ribbon源码解读汇总篇~

    前言 想说的话 [一起学源码-微服务-Ribbon]专栏到这里就已经全部结束了,共更新四篇文章. Ribbon比较小巧,这里是直接 读的spring cloud 内嵌封装的版本,里面的各种config ...

  10. java小项目之:扫雷,这游戏没有你想的那么简单!

    扫雷 我之前分享的小项目和小游戏,电影购票.坦克大战.捕鱼达人.贪吃蛇等,虽然已经是耳熟能详人尽皆知的项目和游戏,但是保不齐真的有人没接触过. 今天分享的这个项目,我不相信没人接触过(仅限80后-00 ...