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

1.无符号和有符号

计算机中用补码表示负数,并且有一定的计算方式;另外,用二进制的最高位表示符号,0表示正数、1表示负数。这种说法本身没错,可是要有一定的解释,不然它就是错的,至少不能解释,为什么字符类型的-1二进制表示是“1111 111116进制表示为FF,而不是1000 0001

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

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

一个字节为8位,按0开始记,那它的最高位就是第7位,2个字节,最高位就是15位,4个字节,最高位是31位,不同长度的类型,最高为业不同,但总是最左边那位。

 

无符号和有符号数的范围的区别

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

无符号数:1111 1111 值:255=1*2^7+1*2^6+.....=2^n-1

有符号数:0111 1111 值:127

同样一个字节,无符号的最大值是255,有符号的最大值是127,下图是无符号数和有符号数的范围:

一个由符号的数据类型的最大值的计算方法完全和无符号一样,只不过它少了一个最高位,但是在负数范围内,数值的计算方法不能直接使用前面的公式,在计算机种,负数除了最高位为1以外,还采用补码的形式,所以在计算前,需要对补码进行还原。

10进制的计算经验,1表示正1-1表示和1相对的负值。那么很容易想到在二进制中,0000 0001表示正1,则高位为1后:1000 0001应该表示-1,不过实际上,计算机中的规定有些相反:

可以发现,1000 0000没有拿来表示-0,而1000 0001也不能拿来直观地表示-1,事实上,-11111 1111来表示。从一个角度来理解,-1大还是-128大,当然是-1大,-1是最大的负整数,所以,无论是字符类型或整数类型,也无论这个整数是几个字节,从计算结果上来看也是对的:1111 1111-1=1111 1110,表示-2,这样一直减下去,当减到只身最高位用于表示符号的1以外,其他低位全为0,就是最小的负值,也就是-128

 
2.Java 基本数据类型  

 
 
3.Java
符号类型

Java的原始类型里没有无符号类型,如果需要某个宽度的无符号类型,可以用>>>,这个是java的无符号右移操作符,或者使用下一个宽度的带符号类型来模拟,例如,需无符号的short,就用int来模拟:

 

1         int toUnsigned(short s) { 

2             return s & 0x0FFFF

3        

 

 

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

 

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

1         System.out.println(0x80);//128  

2         //0x81看作是int型,最高位(32)0,所以是正数 

3         System.out.println(0x81);//129  

4         System.out.println(0x8001);//32769 

5         System.out.println(0x70000001);//1879048193  

6         //字面量0x80000001int型,最高位(32)1,所以是负数 

7         System.out.println(0x80000001);//-2147483647 

8         //字面量0x80000001L强制转为long型,最高位(第64位)为0,所以是正数 

9         System.out.println(0x80000001L);//2147483649 

10     //最小int 

11     System.out.println(0x80000000);//-2147483648 

12     //只要超过32位,就需要在字面常量后加L强转long,否则编译时出错 

13     System.out.println(0x8000000000000000L);//-922337203685477

 

4.有符号扩展和无符号扩展

System.out.println(Long.toHexString(0x100000000L +0xcafebabe));// cafebabe

结果为什么不是0x1cafebabe?该程序执行的加法是一个混合类型的计算:左操作数是long型,而右操作数是int类型。为了执行该计算,Javaint类型的数值用拓宽原生类型转换提升为long类型,然后对两个long类型数值相加。因为int是有符号的整数类型,所以这个转换执行的是符号扩展


这个加法的右操作数0xcafebabe32位,将被提升为long类型的数值0xffffffffcafebabeL,之后这个数值加上了左操作0x100000000L。当视为int类型时,经过符号扩展之后的右操作数的高32位是-1,而左操作数的第32位是1,两个数

值相加得到了0
  0x ffffffffcafebabeL
+0x 0000000100000000L
-----------------------------
 0x 00000000cafebabeL

如果要得到正确的结果0x1cafebabe,则需在第二个操作数组后加上“L”明确看作是正的long型即可,此时相加时拓

展符号位就为0

Java代码

1         System.out.println(Long.toHexString(0x100000000L + 0xcafebabeL));// 1cafebabe

 

5.窄数字类型提升至宽类型时使用符号位扩展还是零扩展

System.out.println((int)(char)(byte)-1);// 65535 

结果为什么是65535而不是-1

 

窄的整型转换成较宽的整型时符号扩展规则:如果最初的数值类型是有符号的,那么就执行符号扩展(即如果符号位为1,则扩展为1,如果为零,则扩展为0);如果它是char,那么不管它将要被提升成什么类型,都执行零扩展

 

了解上面的规则后,我们再来看看迷题:因为byte是有符号的类型,所以在将byte数值-1(二进制为:11111111)提升到char时,会发生符号位扩展,又符号位为1,所以就补81,最后为161;然后从charint的提升时,由于是char型提升到其他类型,所以采用零扩展而不是符号扩展,结果int数值就成了65535

 

如果将一个char数值c转型为一个宽度更宽的类型时,只是以零来扩展,但如果清晰表达以零扩展的意图,则可以考虑

使用一个位掩码:

Java代码

1         int i = c &0xffff;//实质上等同于:int i = c ; 

说明:

至于0xff,这属于java的字面常量,他已经是int了,ff表示为11111111java对这种字面常量,不把他前面的1看做符号位,虽然也是有符号扩展,但是,扩展成的是00...ff.

数字字面常量的类型都是int型,而不管他们是几进制,所以“2147483648”“0x180000000(十六进制,共33位,所以超过了整数的取值范围)字面常量是错误的,编译时会报超过int的取值范围了,所以要确定以long来表示“2147483648L”“0x180000000L”

 

 System.out.println(0x80000001);//-2147483647,已经是32位,最高位是符号位

System.out.println(0xcafebabe);//-889275714

System.out.println(0xffff);    //65535int是32位的,最高位已经是0,相当于0X0000ffff

System.out.println(0xff);      //255

 

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

2         int i = (short)c; 

 

如果将一个byte数值b转型为一个char,并且不希望有符号扩展,那么必须使用一个位掩码来限制它:

3         char c = (char)(b & 0xff);// char c = (char) b;为有符号扩展 

 

((byte)0x90 == 0x90)?

 

答案是不等的,尽管外表看起来是成立的,但是它却等于false。为了比较byte数值(byte)0x90int数值0x90Java

通过拓宽原生类型将byte提升为int,然后比较这两个int数值。因为byte是一个有符号类型,所以这个转换执行的是符号扩展,将负的byte数值提升为了在数字上相等的int值(10010000?111111111111111111111111 10010000)。在本例中,该转换将(byte)0x90提升为int数值-112,它不等于int数值的0x90,即+144


解决办法:使用一个屏蔽码来消除符号扩展的影响,从而将byte转型为int

Java代码

1         ((byte)0x90 & 0xff)==0x90 

 

6.Javabyte转换int时与0xff进行与运算的原因

javabyte转换int时为何与0xff进行与运算

在剖析该问题前请看如下代码
 public static String bytes2HexString(byte[] b) {
  String ret = "";
  for (int i = 0; i < b.length; i++) {
   String hex = Integer.toHexString(b[i] & 0xFF);
   if (hex.length() == 1) {
    hex = '0' + hex;
   }
   ret += hex.toUpperCase();
  }
  return ret;
 }
上面是将byte[]转化十六进制的字符串,注意这里b[i] & 0xFF将一个byte 0xFF进行了与运算,然后使用Integer.toHexString取得了十六进制字符串,可以看出
b[i] & 0xFF运算后得出的仍然是个int,那么为何要和 0xFF进行与运算呢?直接 Integer.toHexString(b[i]);,byte强转为int不行吗?答案是不行的.

其原因在于:
1.byte
的大小为8bitsint的大小为32bits
2.java
的二进制采用的是补码形式

在这里先温习下计算机基础理论

byte是一个字节保存的,有8个位,即801
8
位的第一个位是符号位,
也就是说0000 0001代表的是数字1
1000 0000
代表的就是-1
所以正数最大位0111 1111,也就是数字127
负数最大为1111 1111,也就是数字-128

上面说的是二进制原码,但是在java中采用的是补码的形式,下面介绍下什么是补码

Integer.toHexString的参数是int,如果不进行&0xff,那么当一个byte会转换成int时,由于int32位,而byte只有8位这时会进行补位,例如补码11111111的十进制数为-1转换为int时变为11111111111111111111111111111111好多1啊,呵呵!即0xffffffff但是这个数是不对的,这种补位就会造成误差。和0xff相与后,高24比特就会被清0了,结果就对了。

 

补码是有符号数,所以从8位变为int需要有符号扩展,变为11111111111111111111111111111111(最终的值为-1)。

至于0xff,这属于java的字面常量,他已经是int了,ff表示为11111111java对这种字面常量,不把他前面的1看做符号位,虽然也是有符号扩展,但是,扩展成的是00...ff.

一般在有些编译器重,写ff,会把第一位1认为是符号位,所以可以这么写:0x0ff

 

7.DataInputStream

 

DataInputStream in = new DataInputStream(

                                new BufferedInputStream(fileInputStream));


在Java中,不存在Unsigned无符号数据类型,但可以轻而易举的完成Unsigned转换。

方案一:如果在Java中进行流(Stream)数据处理,可以用DataInputStream类对Stream中的数据以Unsigned读取。

Java在这方面提供了支持,可以用java.io.DataInputStream类对象来完成对流内数据的Unsigned读取,该类提供了如下方法:
(1)int   readUnsignedByte()    //从流中读取一个0~255(0xFF)的单字节数据,并以int数据类型的数据返回。返回的数据相当于C/C++语言中所谓的“BYTE”。
(2)int readUnsignedShort()   //从流中读取一个0~65535(0xFFFF)的双字节数据,并以int数据类型的数据返回。返回的数据相当于C/C++语言中所谓的“WORD”, 并且是以“低地址低字节”的方式返回的,所以程序员不需要额外的转换。

方案二:利用Java位运算符,完成Unsigned转换。

正常情况下,Java提供的数据类型是有符号signed类型的,可以通过位运算的方式得到它们相对应的无符号值,参见几个方法中的代码:



/**
 * Created by Lovell on 7/7/16.
 */
public class UnsignedUtil {
    /**
     * java byte (1 byte == 8 bit) (-2^7~2^7-1 : -128~127) to unsigned short(0~2^8-1 : 0~255)
     *
     * @param data
     * @return
     */
    public static int getUnsignedByte (byte data){
        return data&0x0FF;
    }

    /**
     * java short (1 short == 2 byte == 16 bit) (-2^15~2^15-1 : -32768~32767) to unsigned short(0~2^16-1 : 0~65535)
     *
     * @param data
     * @return
     */
    public static int getUnsignedShort (short data){
        return data&0x0FFFF;
    }

    /**
     * java int (1 int == 4 byte == 32 bit)(-2^31~2^31-1 : -2147483648~2147483647) to unsigned short(0~2^32-1 : 0~4294967295)
     *
     * @param data
     * @return
     */
    public long getUnsignedInt (int data){
        return data&0x0FFFFFFFF;
    }

}

  java 类型
8bit unsigned integer ---> short
8bit signed integer ---> byte
16bit unsigned integer ---> int
16bit signed integer ---> short
32bit unsigned integer ---> long
32bit signed integer ---> int

 java,c,c++ 语言之间基本数据类型的比较 收藏

当要进行底层移植的时候肯定会遇到这些问题。特整理了下。

java语言基本数据类型

在JAVA中一共有八种基本数据类型,他们分别是

byte、short、int、long、float、double、char、boolean

整型

其中byte、short、int、long都是表示整数的,只不过他们的取值范围不一样

byte的取值范围为-128~127,占用1个字节(-2的7次方到2的7次方-1)

short的取值范围为-32768~32767,占用2个字节(-2的15次方到2的15次方-1)

int的取值范围为(-2147483648~2147483647),占用4个字节(-2的31次方到2的31次方-1)

long的取值范围为(-9223372036854774808~9223372036854774807),占用8个字节(-2的63次方到2的63次方-1)

可以看到byte和short的取值范围比较小,而long的取值范围太大,占用的空间多,基本上int可以满足我们的日常的计算了,而且int也是使用的最多的整型类型了。

在通常情况下,如果JAVA中出现了一个整数数字比如35,那么这个数字就是int型的,如果我们希望它是byte型的,可以在数据后加上大写的 B:35B,表示它是byte型的,同样的35S表示short型,35L表示long型的,表示int我们可以什么都不用加,但是如果要表示long型 的,就一定要在数据后面加“L”。

浮点型

float和double是表示浮点型的数据类型,他们之间的区别在于他们的精确度不同

float 3.402823e+38 ~ 1.401298e-45(e+38表示是乘以10的38次方,同样,e-45表示乘以10的负45次方)占用4个字节

double 1.797693e+308~ 4.9000000e-324 占用8个字节

double型比float型存储范围更大,精度更高,所以通常的浮点型的数据在不声明的情况下都是double型的,如果要表示一个数据是float型的,可以在数据后面加上“F”。

浮点型的数据是不能完全精确的,所以有的时候在计算的时候可能会在小数点最后几位出现浮动,这是正常的。

boolean型(布尔型)

这个类型只有两个值,true和false(真和非真)

boolean t = true;

boolean f = false;

char型(文本型)

用于存放字符的数据类型,占用2个字节,采用unicode编码,它的前128字节编码与ASCII兼容

字符的存储范围在\u0000~\uFFFF,在定义字符型的数据时候要注意加' ',比如 '1'表示字符'1'而不是数值1,

char c = ' 1 ';

我们试着输出c看看,System.out.println(c);结果就是1,而如果我们这样输出呢System.out.println(c+0);

结果却变成了49。

如果我们这样定义c看看

char c = ' \u0031 ';输出的结果仍然是1,这是因为字符'1'对应着unicode编码就是\u0031

char c1 = 'h',c2 = 'e',c3='l',c4='l',c5 = 'o';

System.out.print(c1);System.out.print(c2);System.out.print(c3);System.out.print(c4);Sytem.out.print(c5);

String

在前面我们看到过这样的定义:

String s = "hello";

System.out.println(s);跟上面的5条语句组合起来的效果是一样的,那么String是个什么呢?String是字符串,它不是基本数据类型,它是一个类。


 c语言数据类型

1.整型数据类型
C定义了5种整型数据类型。
整型数据类型表

序号 类型名称 说明 字节数 取值范围
1 signed char 有符号的单字节整数类型 1 -128~+127
2 short int 短整型 2 -32768~+32767
3 int 整型 4 -2147438648~+2147438647
4 long int 长整型 4 -2147438648~+2147438647
5 long long int 长长整型 8 -9223372036854775808~+-9223372036854775807

2.无符号整数类型
对应有符号类型,还有无符号整数类型。
无符号整数类型表

序号 类型名称 字节数 取值范围
1 unsigned char 1 0~255
2 unsign short int 2 0~65535
3 unsigned int 4 0~4294967295
4 unsigned long int 4 0~4294967295
5 unsign long long int 8 0~18446744073709551615

3.整型常量 
整型常量是指用以表示整型数值的常量,分为短整型(short int)、整型(int)、长整型(long int )和长长整型(long long int)四种。C默认整型(int)。 各种类型整型常量进制表示表(后缀不区分大小写)

序号 数据类型 八进制 十进制 十六进制
1 整型 0112 74 0x4a
2 长整型(l) 0112l 74l 0x4al
3 长长整型(ll) 0112ll 74ll 0x4all
4 无符号整型(u) 0112u 74u 0x4au
5 无符号长整型(ul) 0112ul 74ul 0x4aul
6 无符号长长整型(ull) 0112ull 74ull 0x4aull

4.字符数据类型 
C语言中字符型数据只有一种,即char型数据。一般也把char直接称为字符型。字符型占用内存空间最少,一般占用一个字节,存储在char类型变量的整数可以表示为有符号或无符号的值,这取决于编译器。

5.字符变量
字符变量是用于存储字符型数值的变量。字符型变量也分为两种:有符号和无符号型。

6.浮点型数据类型
C语言定义了三种浮点数据类型:
•float,单精度
•double,双精度
•long double,长双精度
C标准中对不同类型的浮点数有不同的规定,编译器不同或硬件条件不同,字节长度也不相同。

浮点型的字节长度、精度、数量级范围和输出输入格式表

序号 数据类型 字节长度 精度 数量级范围 printf和scanf格式
1 float(f) 4 7 -38~38 %f
2 double 8 约16 -308~308 %f
3 long double(1) 12 约19 -4932~4932 %llf

7.浮点型精度
浮点型精度从低到高排列为float、double和long long double。

8.浮点型的存储方式
浮点型数值以科学计数法的表示形式存储在内存中。浮点型的内存形式包含三个部分:
1)符号位
符号位浮点型的符号位只有一位,为最高位。该位为1,表示负数,该位为0,为非负数。
2)指数位
浮点型的指数位以补码形式存储,是科学计数法的指数部分。
3)基数位
基数位是浮点型的最后一位,这个位决定数值的精度。
浮点型储存分段表

序号 数据类型 符号位 指数位 基数位 偏差值
1 float 1 8 23 127
2 double 1 11 52 1023
3 long double 1 15 64 16383

 c++语言基本数据类型

 

C++基本数据类型有字符型(char)、整数类型(int)、浮点型(float)、双精度型(double)和无值类型(void),下表给出各种基本类型和内存中的位数和表示数值的范围(对于字符型存储的是ASCII码):

基本数据类型

类型 类型名 位数 范围
字符型 char 8 -128 ------ 127
整型 int 16 -32768 ------ 32767
浮点型 float 32 3.4E-38 ------ 3.4E+38
双精度型 double 64 1.7E-308 ------ 1.7E+308
无值类型 void 0 0

    类型名前可以加修饰符(void类型除外),修饰符有 signed(带符号的)、 unsigned(无符号的)、 short(短的)和 long(长的)。signed 和 unsigned 用于字符型或整型,short 和 long 可用于整型,还可用于双精度型,如下表所示:

基本数据类型和修饰符

类型 类型名 位数 范围
字符型 char 8 -128 ------ 127
signed char 8 -128 ------ 127
unsigned char 8 0 ------ 255
整型 int 16 -32768 ------ 32767
signed int 16 -32768 ------ 32767
unsigned int 16 0 ------ 65535
short int 16 -32768 ------ 32767
signed short int 16 -32768 ------ 32767
unsigned short int 16 0 ------ 65535
long int 32 -2,147,483,648 ---- 2,147,483,647
signed long int 32 -2,147,483,648 ---- 2,147,483,647
unsigned long int 32 0 ----------------4,294,967,295
浮点型 float 32 3.4E-38 ------ 3.4E+38
双精度型 double 64 1.7E-308 ------ 1.7E+308
long double 80 3.4E-4932 ------ 1.1E+4932

    当用 signed 、unsigned 、short 和 long 修饰 int 时,int 可省略。

字符型实际上是一个字节的整型 ,浮点型和双精度型表示实数。

注:不同编译系统,整数的存储长度不同。对于16位的编译系统,int为2字节;而对于32位的编译系统,int为4字节。可以用sizeof运算符测试。如:

cout<

输出结果是字节数。


你可能感兴趣的:(Java)