代码改变世界

关于负数

2017-10-12 18:44 by soar., ... 阅读, ... 评论, 收藏, 编辑

本文主要是想阐述清楚以下几个知识点:

  1、与负数相关的一些基础概念;

  2、负数的二进制和十进制之间的相互转换;

  3、负数与数据类型;

 

一、基础概念

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

  真值:因为负数的机器数,最高位为符号位,所以机器数的形式值就不等于真正的数值,不像正数那么“一目了然”。例如1000 0011,如果是负数,则bit7为1,其真正十进制数值为-125,而不是形式值131(当然如果不是负数,那么确实就是131);所以,为了区别起见,将带符号位的机器数对应的真正数值称为机器数的真值;

  补码:负数在计算机中,是以其绝对值的补码形式表示。机器数的补码可由其原码(原码即数值的二进制机器数)计算得到。如果数值为正数,则其原码和补码一样;如果数值为负数,则其补码是他对应的原码(除符号位外)取反,并在末尾加一得到。

 

二、负数的二进制和十进制转换

负数的十进制转二进制:Math.abs(number)-1,再取反,简称“减一取反”。

负数的二进制转十进制:先取反,再Math.abs(number)+1,简称“取反加一”

  取反也叫做反码,负数的二进制直接是看不出来其十进制的表示(也就是真值),所以通过“取反加一”可以得到其补码,在计算补码的过程中,符号位是不参与计算。

  举例:

  1、数值-110十进制转二进制

    1)减1:Math.abs(110)-1=109

    2)取反:109的二进制表示为:0110 1101,取反为 1001 0010

  2、数值-110二进制转十进制

    1)取反:1001 0010,取反为0110 1101

    2)加1:加1后为0110 1110,等于110,因为其原码的二进制形式最高位为1,所以是负数,也就是-110

 

三、负数与数据类型

 

  1、有符号和无符号数据类型

    在编程语言中,一般都存在有符号数据类型和无符号数据类型。无符号类型也就是没有符号位。例如有符号byte类型,范围为-128~127,而无符号byte类型,因为最高位不用存储符号,而可以直接存储数值,则为0~255,当然也就无法存储(表示)负数。

 

  2、有符号和无符号数据类型之间的转换

    以C#语言举例,byte为无符号,sbyte为有符号。

    byte b1 = –100;

    sbyte b2 = 250;

    这两行代码都无法通过编译,原因也简单,赋给变量的值都超出了其能存储的范围。而在实际项目中,我们经常会遇到,需要用byte来存储对方发送过来的sbyte数据。而在c#中,通过下面方式处理了不同符号数据类型之间的转换:

    byte b1 = –100&0xff;  // 156

    sbyte b2 = (sbyte)Convert.ToInt32(250); // -6

    代码是解决了转换,但为什么可以正确转换,结果又是如何而来,我们分析一下:-100转换为无符号方法,首先得到其二进制形式1001 1100,因为要将其转换为无符号,则其最高位不再是符号位,而是当作具体数值位来计算,则为156;250转换为有符号方法,首先得到其二进制形式1111 1010,将其最高位视为符号位,因为是负数,按照“取反加一”方式转换为十进制,则为-6。

无符号转有符号数据类型:获取数值的二进制原码,将最高位视为符号位,再将其转换为十进制,得到的数值即为转换后的值。

有符号转无符号数据类型:获取数值的二进制原码,将最高位视为存储数值的位,再将其转换为十进制,得到的数值即为转换后的值。

 

  3、强制类型转换带来的数值丢失

    当有符号和无符号之间互相转换时,如果是符号相同,且目标数据类型bit位数大于或等于当前数据类型,例如sbyte和int32,或byte和uint32,则不会出现数据“丢失”(暂且这么叫),这里主要讨论另外两种情况:

    1、向小于自身数据类型bit位数的类型转换,例如int32转换为sbyte或byte。

      显而易见,如果数值是一个大于sbyte或byte的数值,则转换后,会存在丢失。这里主要从二进制的角度分析如何丢失?转换后的数值是怎么来的?

      例如

      int32 a1 = 3000;

      sbyte a2 = (sbyte)a1; //输出-72

      byte a3 = (byte)a3;//184

      分析:3000的二进制为0000 0000 0000  0000 0000 1011 1011 1000,而sbyte只能存储0~255,所以只能取bit0~bit7,也就是1011 1000,而sbyte为有符号数据类型,所以bit7为符号位,按“取反加一”得到72,加上符号,也就得到-72。

      而a3,因为是byte无符号类型,所以1011 1000,直接取十进制即可,也就得到184。这种情况下,因为发生了数据位的“截取”,所以即使再类型转换回去,也无法得到原值,所以是真正的丢失。

    2、向大于或等于自身数据类型bit位数,且符号不同的数据类型转换,例如sbyte转换为byte,sbyte(有符号)转换为uint32(无符号),或byte(无符号)转换为int32(有符号)。

      例如:

      sbyte a1 = –10;

      byte a2 =  -10&0xff;// 246

      int32 a3 = a1;// -10

      uint32 a4 = (unit32)a1; // 4294967286

      分析:变量a3的数据类型和a1的符号相同,int32位数又大于sbyte,所以数据不会丢失,还是-10。而a2,因为要将-10转换为无符号,所以直接无视其符号位,得到246。a4这里稍微详细说一下,首先-10的二进制为1111 0110,当转换为32bit时,-10的负数形式则为:

      1111 1111 1111 1111 1111 1111 1111 0110

      因为要转换为uint32无符号类型,所以无视符号位,转换为十进制得到4294967286。

      转换后,虽然数值和转换前原值不同,但转换回原类型,仍然能够拿到正确的原值。所以这种转换情况,会得到”非预期的数值“,但并没有丢失,转换回去,依然能够拿回原值。

 

总结:

  文本主要描述了一些关于负数的基础概念,以及负数的二进制和十进制之间相互转换。也分析了不同符号或不同数据类型之间转换,可能会发生的一些“丢失数值“或”得到非预期的数值“情况。都是些非常基础的知识,仅仅为了记录一下。如果文中有理解错误的地方,烦请指出,感谢!