有趣的byte与0xff

背景:   最近看到这样一段代码,字节数据转字符串,起初没有看懂,为啥Integer.toHexString(0xFF & bArray[i])    这个方法里要写成0xFF & bArray[i]

这句代码的最终目的是把byte[]转换为16进制字符串,toHexString()是把一个int转换为十六进制String ,&0xFF是为了保证byte类型转int后其二进制的一致,即补零扩展...

 public String bytesToHexString(byte[] bArray) {
        StringBuffer sb = new StringBuffer(bArray.length);
        String sTemp;
        for (int i = 0; i < bArray.length; i++) {
            sTemp = Integer.toHexString(0xFF & bArray[i]);
            if (sTemp.length() < 2) {
                sb.append(0);
            }
            sb.append(sTemp.toUpperCase());
        }
        return sb.toString();
  }

看完这段解释,好像懂了又好像没懂,为啥要补零扩展?还有什么扩展呢?

一、了解基本概念原码、反码、补码

  •   原码,就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值,正数的符号位为 0、负数的符号位为 1
  •  反码,负数的反码是在其原码的基础上, 符号位不变,其余各个位取反,正数的反码是其本身
  •  补码,负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1),正数的补码就是其本身

反码是原码和补码相互转换时的临时过渡,用处不大

二、为什么计算机中要以补码形式存储数据?

      在计算机系统中数值一律用补码的形式来表示和存储。原因是:使用补码时,可以将符号位和数值位统一处理;同时加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运算结果是相同的,不需要额外的硬件电路。其次存放补码可以保证计算的准确性。因为CPU只有加法,所以要将1-1转换成1+(-1)来计算。因为1是正数,所以1的反码补码都是其本身。以int类型数据为例子:

1的原/反/补码   0000 0000 0000 0000 0000 0000 0000 0001

-1的       原码   1000 0000 0000 0000 0000 0000 0000 0001

-1的       反码    1111 1111 1111   1111  1111  1111 1111  1110

-1的       补码    1111 1111 1111   1111  1111  1111 1111   1111 

将1的补码和-1的补码相加计算, 

  0000 0000 0000 0000 0000 0000 0000 0001

 1111 1111   1111   1111  1111 1111  1111  1111 

结果是

1 0000 0000 0000 0000 0000 0000 0000 0000

转换为int 类型,这时候你会发现最高位1其实丢了,因为int 类型占4个字节即32bit,所以0000 0000 0000 0000 0000 0000 0000 0000 再次转为原码还是0000 0000 0000 0000 0000 0000 0000 0000,即得到运算结果是十进制的0

三、补零扩展和补符号扩展

byte占8位,int占32位等。正因如此,当把一个低精度的数据类型转成一个高精度的数据类型时,必然会涉及到如何扩展位数的问题。这里有两种解决方案: 

  • 补零扩展:填充一定位数的0。 
  • 补符号位扩展:填充一定位数的符号位(非负数填充0,负数填充1)

举例说明,

-127的原码是 1111 1111 (注意1111 1111 表示无符号是就是255 ,有符号时就是-127 ,有符号时+127 是 0111 111)

-127的反码是 1000 0000

-127的补码是 1000 0001 计算机中存储的就是这个补码,

使用补零扩展得到结果是        0000 0000 0000 0000  0000 0000 1000 0001 正数的补码就是其本身,所以转换为10进制是129 

使用补符号扩展得到的结果是 1111  1111  1111 1111  1111  1111  1000 0001 

求这个补码的补码即原码 1000 0000 0000 0000 00000 0000 0111 1111所以转换为10进制是-127 

由此可见 对与-127,在计算机存储的是补码1000 0001,补零扩展后,为0000 0000 0000 0000  0000 0000 1000 0001,使用补零扩展能够保证二进制存储的一致性,但不能保证十进制值不变,补符号扩展,虽然得到的十进制值没有改变,但是二进制的值却变了,变成了1111  1111  1111 1111  1111  1111  1000 0001
 

四 、我们到底想要什么?

情况一,我们需要 十进制的一致性

public class Test{
    public static void main(String[] args) {
        Byte a=-127;
        int b=a;
        System.out.println(b);
    }
}
// 输出结果是-127

这是因为Java中只有有符号数,当byte扩展到short, int时,Java中的扩展方式是补符号位扩展

但是我做byte->int的转化 所有时候都只是为了保持 十进制的一致性吗?

不一定吧?好比我们拿到的文件流转成byte数组,难道我们关心的是byte数组的十进制的值是多少吗?我们关心的是其背后二进制存储的补码吧。所以大家应该能猜到为什么byte类型的数字要&0xff再赋值给int类型,其本质原因就是想保持二进制补码的一致性。

情况二,我们需要保持二进制补码的一致性

当byte要转化为int的时候,高的24位必然会补1,这样,其二进制补码其实已经不一致了,&0xff可以将高的24位置为0,低8位保持原样。这样做的目的就是为了保证二进制数据的一致性。

因为 0xFF = 1111 1111  低8位为1,高位都为0

对应的二进制为 0000 0000 0000 0000 0000 0000 1111 1111,位运算符&的特点两边都是1才是1,否则是0,故 &0xFF 可将数字的高位都置为0,低8位不变

当然拉,保证了二进制数据性的同时,如果二进制被当作byte和int来解读,其10进制的值必然是不同的,因为符号位位置已经发生了变化

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
//;UART0收发一组字符(48个)(通过) // rxd_str必须是全局变量,切使用前要赋值。 // rxd_str不能是局部变量,局部变量会清零, //因为中断一次只能收一个字符rxd_str不可能>=LENM /********************************************** 文件描述: 三相电测量上报 功能说明: 测量ATT7022B完成 PCF8563上报定时(需校时) FM24C256转换数据存储 UART0口用于RS232读数据 UART1控制无线模块上报数据 创建:2006年12月5日 /********************************* *函数:main. *入口:无 *出口:无 *功能说明:接收UART端口命令 执行读ATT7022B的数据 进行处理、存储和回发数据 ********************************/ //***************************** //包含的文件 //****************************** #include <c8051f020.h> #include <main.h> #include <intrins.h> //************************************************* //函数:init_sysclk(void) //功能:时钟初始化 //入口:无 //出口: 无 //说明:使用外部时钟12M //************************************************* void init_sysclk (void) { uint i=0; OSCXCN=0x67; //external oscillator with 12MHz crystal for(i=0;i<256;i++); // XTLVLD blanking while(!(OSCXCN & 0x80)); // Wait for crystal osc. to settle OSCICN=0x88; //时钟丢失检测,选择外部时钟 CKCON = 0x00; //时钟分频 } /********************************* 函数:void enable_wdog(void) void disable_wdog(void) 功能:使能、禁止wdog 入口:无 出口:无 说明: ********************************/ void enable_wdog(void) { WDTCN=0xA5; //允许看门狗定时器工作 } void disable_wdog(void) //禁止看门狗定时器工作 { WDTCN=0xDE; WDTCN=0xAD; } /*********************************** 函数:void init_ioport() 功能:端口配置及端口位定义 入口:无 出口:无 说明: *************************************/ void init_ioport() { XBR0=0x07; //TXD0-P0.0 RXD0-P0.1,SPI_SCK-P0.2,SPI_MISO-P0.3 //SPI_MOSI-P0.4,SPI_NSS-P0.5,SDA-P0.6,SCL-P0.7, XBR2=0x44; //端口I/O弱上拉允许,TX1-P1.0,RXD-P1.1交叉开关允许 XBR1=0X10; //INT1使能INT1--P1.2 P0MDOUT=0x00; //端口0输出方式寄存器:0--漏极开路 P0=0xff; P1MDIN=0XFF; //端口1输入方式寄存器:0--配置为数字输入 P1MDOUT=0x00; //端口1输出方式寄存器,0--漏极开路 P1=0xff; P2MDOUT=0x00; //端口2输出方式寄存器:0--漏极开路 P2=0xff; P3MDOUT=0X00; //端口3输出方式寄存器:0--漏极开路 P3=0xff; P74OUT=0x00; //端口7-4输出方式寄存器:0--漏极开路 P4=0xff; P5=0xff; P6=0xff; P7=0xff; } //-------------------------------------------------------------- //函数:void Delay_ms (unsigned ms) //功能:实现延时功能 Timer0_ms //--------------------------------------------------------------- // /* Configure Timer0 to delay <ms> */ void Delay_ms (unsigned ms) { uchar i; // millisecond counter TCON &= ~0x30; // STOP Timer0 and clear overflow flag TMOD |= 0x01; // configure Timer0 to 16-bit mode CKCON |= 0x08; // Timer0 counts SYSCLKs for (i = 0; i < ms; i++) // count milliseconds { TR0 = 0; // STOP Timer0 TH0 = (-SYSCLK/1000) >> 8; // set Timer0 to overflow in 1ms TL0 = -SYSCLK/1000; TR0 = 1; // START Timer0 while (TF0 == 0); // wait for overflow TF0 = 0; // clear overflow indicator } } //------------------------------------------------------- //函数:void Delay_us (unsigned us) //功能:实现延时功能 Timer0_us //------------------------------------------------------- // /* Configure Timer0 to delay <us>*/ void Delay_us (unsigned us) { uchar i; // microseconds counter TCON &= ~0x30; // STOP Timer0 and clear overflow flag TMOD |= 0x01; // configure Timer0 to 16-bit mode CKCON |= 0x08; // Timer0 counts SYSCLKs for (i = 0; i < us; i++) // count microseconds { TR0 = 0; // STOP Timer0 TH0 = (-SYSCLK/1000000) >> 8; // set Timer0 to overflow in 1us TL0 = -SYSCLK/1000000; TR0 = 1; // START Timer0 while (TF0 == 0); // wait for overflow TF0 = 0; // clear overflow indicator } } //*************************************** //函数:unsigned char my_add(uchar my_add) //功能:读开关状态确定子地址 //入口:无 //出口:子地址 //说明:子地址存于myadd中(即设备号) //**************************************** void my_add(void) { P74OUT |=0XD0; myadd =P5; } //****************************************** //函数:void jiaob(ATT_JB[]) //功能:写校表寄存器 //说明:UART口接收校表时间并存于FM24C256中 //***************************************** void jiaob (void) { } main() { disable_wdog(); //关看门狗 init_sysclk (); //时钟初始化 init_ioport(); //交叉开关配置 my_add(); //读设备子地址设置 UART0_Init(); //UART0初始化 EX1= 1; //开INT1 EA = 1; //开中断 rxd_str=0; while(1) { if(uart0_flag) { rxd_str=0; uart0_flag = 0; m=ur0_rxd; txd_string(m,LENM); } } //---------判断本设备命令及命令内容执行命令------- } //----------------------------------------------------- //串口初始化 //-------------------------------------------------------- void UART0_Init(void) { SCON0 = 0x50; //串口方式1,波特率可变 PCON |= 0x00; //SMOD = 0 TMOD = 0x20; //选择T1方式2, TH1 = 0xe8; //T1初值, TL1 = 0xe8; ES0 = 1; //UART0中断开启 TR1 = 1; //启动定时器T1 } //---------------------------------------------------------- //发送单个字符 //--------------------------------------------------------- void txd_char(unsigned char ch) { SBUF0 = ch; //送入缓冲区 while(TI0 == 0); //等待发送完毕 TI0 = 0; //软件清零 } //----------------------------------------------------- //发送字符串,调用Send_Char() len字符串长度 //---------------------------------------------------- void txd_string(unsigned char * str,unsigned char len) { unsigned char k = 0; do { txd_char(*(str+k) ); k++; } while(k < len); } //-------------------------------------------------------- //UART0中断服务程序. 接收字符 //-------------------------------------------------------- // rxd_str必须是全局变量,切使用前要赋值。 // rxd_str不能是局部变量,局部变量会清零, //因为中断一次只能收一个字符rxd_str不可能>=LENM void uart0_isr(void) interrupt 4 using 1 { unsigned char rxch; if(RI0) //中断标志 RI0=1 数据完整接收 { RI0 = 0; //软件清零 rxch = SBUF0; //读缓冲 if(rxd_str>=LENM) { uart0_flag=1; rxd_str=0; } ur0_rxd[rxd_str] = rxch; //存入数组,供发送 rxd_str++; } } //********************** //main.h //********************* #ifndef _main_h #define _main_h //***************************** //全局常量 //**************************** #define uchar unsigned char #define uint unsigned int #define SYSCLK 11059200 #define CMD_RESET 0X11 #define CMD_TIME 0X12 #define CMD_DATA 0X13 #define ATT_R 0x00 // ATT Read command #define ATT_W 0x80 // ATT Write command #define fm_Write_add 0xA0 #define fm_Read_add 0xA1 #define Fm_add 0xA0 //fm24c256器件从地?#define LENM 0x30 unsigned char idata r_commond=0x01; //读命令代码 unsigned char idata w_time_commond=0x02; //校时代码 unsigned char xdata jb_commond=0xdd; //校表命令代码 //---------------------------------------------------- //全局变量 //---------------------------------------------------- unsigned char rxd_str; unsigned char * m; unsigned char xdata fm_read_buf[60]={0}; unsigned char xdata fm_write_buf[60]={0}; unsigned char xdata pcf_d[16]={0}; //data of pcf8563 unsigned char xdata *d_ptr; unsigned char xdata ur0_rxd[60]; //每次接收字符串 unsigned char xdata ur0_txd[60]; //要发送的字符串 unsigned char xdata att_rd[90]; unsigned char xdata att_wd[60]; // att7022 of data unsigned char xdata att_jb[40]; //校表数组(myadd=0) unsigned char idata period; //时间间隔 unsigned char idata myadd; unsigned char *str; unsigned char chksum; unsigned char ATT_W_ADD; // ATT Read status register unsigned char ATT_R_ADD; // ATT Write status register uchar slave_add,fm_ram_add,send_byte,write_num,read_num; uchar fm_send_count,fm_receive_count,fm_send_len,fm_receive_len; sbit SDA=P1^6; //*模拟I2C数据传送位*/ sbit SCL=P1^7; //*模拟I2C时钟控制位*/ sbit ATT_CS = P3^0; // ATT CS signal sbit D_E = P1^3; //485收发控制?bit ack; bit uart0_flag; //中断接收完成标志 bit uart1_flag; //中断接收完成标志 bit sm_busy; //收发开始置1,操作结束后由中断清0 bit fm_err_flag; //--------子函数声明------------------------------------- void uart0_isr(); //串口中断服务程序,接收字符 void UART0_Init(void); void txd_char(uchar ch); void txd_string(uchar * str,uchar len); void init_sysclk (void); void enable_wdog(void); void disable_wdog(void); void init_ioport(); void Delay_ms (unsigned ms); void Delay_us (unsigned us) ; void my_add(void); void jiaob (void); void ATT_Write (uchar ATT_WADD, uchar att_wd[]); unsigned char ATT_Read (uchar ATT_RADD,uchar att_rd[]); void SPI0_Init (void); void Start_I2c(); void Stop_I2c(); void Ack_I2c(); void SendByte(uchar x); uchar RcvByte(); bit ISendStr(uchar sla,uchar suba,uchar *d_ptr,uchar no); bit IRcvStr(uchar sla,uchar suba,uchar *d_ptr,uchar no); void WriteClock(void); void StartClock(void); void init_sysclk (void); void init_ioport(); void init_smbus(void); void smbus_receive (uchar chip_select,byte_address,receive_num); void smbus_send (uchar chip_select,byte_address,write_num); void enable_wdog(void); void disable_wdog(void); #endif

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一棵小白菜#

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值