计算机基础---反码,补码与运算符(雪花算法)

在分布式生成全局唯一ID算法Snowflake时遇到一个很有意思的数据

long x = -1L ^ (-1L << 5);

这个x的值究竟是多少呢?答案是31

现在就让我们一起探讨一下这个问题。

首先 long 这一基本数据类型为长整型,在Java里占用64位二进制,所以对于-1L 我们可以用以下的二进制表示,其中第一位为符号位,符号位为1则是负数,为0则为正数,此处将符号位脱离开,这样便于观察。

-1L = 1 1111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111

如何得到-1L的二进制呢?我们需要知道计算机计数的方式,这儿就涉及到所谓的补码,反码概念。

在计算机中,负数的二进制是使用补码表示的。如果在Java中我们要表示 int 型(具有32位二进制)的 1,我们的表示如下:

1 = 
二进制图文详解 二进制Binary 2进制 逢二进一的计数规则。 在计算机内部,一切数据都是2进制的!! 2进制的数字 补码 补码本质是一种解决负数问题的算法1. 将数据的一半当做负数使用。 2. 补码在内存中是2进制的,显示的时候为10进制。 - Java利用算法支持了补码计算: - Integer.parseInt() - Integer.toString() 3. 补码的缺点: - 不支持超范围计算 - 超范围计算自动溢出 4. 解决补码的缺点:采用更大范围(更多位数)补码 Java是如何计算 -2-1补码的规律 1. 最大值的规律:最高位0 剩下全是1 - int类型:一个0,311 - long类型:一个0,63个1 2. 最小值的规律:最高位1 剩下全是0 - int类型:一个1,31个0 - long类型:一个1,63个0 3. 负数的最高位是1, 正数最高位是0 - 最高位做为识别正数和负数的标志位:称为符号位 - 注意:符号位不是用来表示正负号的!!! 4. -1 的规律:所有位都是1!! 5. 溢出是有规律的! 是一个周期性计算结果。 - 最大值+1 = 最小值 6. 补码的对称现象:-n = ~n + 1 案例: int max = Integer.MAX_VALUE; System.out.println(Integer.toBinaryString(max)); int min = Integer.MIN_VALUE; System.out.println(Integer.toBinaryString(min)); long lmax = Long.MAX_VALUE; System.out.println(Long.toBinaryString(lmax)); long lmin = Long.MIN_VALUE; System.out.println(Long.toBinaryString(lmin)); //-1的规律 int n = -1; System.out.println(Integer.toBinaryString(n)); long l = -1L; System.out.println(Long.toBinaryString(l)); //最大值+1溢出得到最小值 // 推论:Java中的int数字是按照补码圆环排列的 int m = Integer.MAX_VALUE+1; System.out.println(m);//最小值 //一个数的溢出测试: n = 345; m = n + Integer.MAX_VALUE+1; System.out.println(m);//负数 m = n + Integer.MAX_VALUE+1+ Integer.MAX_VALUE; System.out.println(m);//344 正数 m = n - (Integer.MAX_VALUE+1+ Integer.MAX_VALUE+1); System.out.println(m);//345 正数 经典面试题1 正数的溢出结果是负数(错误!!!) 经典面试题2 int i = Integer.MAX_VALUE+1; System.out.println( Integer.toBinaryString(i)); 选择运行结果(D): A. 11111111111111111111111111111111 B. 1111111111111111111111111111111 C. 01111111111111111111111111111111 D. 10000000000000000000000000000000 经典面试题3 System.out.println(~-55); 如上代码的运算结果: ( 54 ) System.out.println(~-230); 如上代码的运算结果: ( 229 ) System.out.println(~55); 如上代码的运算结果: ( -56 ) 16进制 16进制是2进制的简写形式 2进制运算 1. ~ 取反运算 2. & 运算(逻辑乘法) 运算规则: 1 & 1 = 1 1 & 0 = 0 0 & 1 = 0 0 & 0 = 0 运算用途: n: 00010100 11010111 01010001 11101010 m: 00000000 00000000 00000000 11111111 n&m-------------------------------------- k 00000000 00000000 00000000 11101010 如上的运算是一个有意义的运算: 意义在于k是数字n的低8位数字!!m是一个分割模板,称为Mask(面具) 案例: int n = 0x14d751ea;//16简写(缩写)的2进制 int m = 0xff;//255 int k = n&m; //输出 n m k 的2进制 3. | 或运算(逻辑加法) 规则: 0 | 0 = 0 1 | 0 = 1 0 | 1 = 1 1 | 1 = 1 用途: n = 00000000 00000000 00000000 10110101 m = 00000000 00000000 11011011 00000000 n|m ------------------------------------ k 00000000 00000000 11011011 10110101 //判断是否读取到文件的末尾: ch1 00000000 00000000 00000000 10110101 ch2 00000000 00000000 00000000 11001111 ch3 11111111 11111111 11111111 11111111 ch4 11111111 11111111 11111111 11111111 ch1|ch2|ch3|ch4 ------------------------- -1 11111111 11111111 11111111 11111111 if(ch1|ch2|ch3|ch4 >> 逻辑右移动运算 将2进制数的每个位向右移动,左侧空白补充0 规则: n: 00010100 11010111 01010001 11101010 m = n>>>1; m: 000010100 11010111 01010001 1110101 m = n>>>8; m: 00000000 00010100 11010111 01010001 案例: int n = 0x14d751ea; int m = n>>>1; //按照2进制输出n 和 m m = n>>>8; //按照2进制输出 m k = (n>>>8)&0xff; //按照2进制输出 k > 思考:将一个int拆分为4个8位数 d1 d2 d3 d4? 5. >> 数学右移位运算 复习:移动小数点计算 一个整数 312331. 将小数点先左移动一次 31233.1 原始数据除以10 将小数点先左移动2次 3123.31 原始数据除以100 如果看做小数点不动,数字向右移动一次,原始数据除以10 ... 2进制数有同样现象:数字向右移动一次,原数据除以2 n = 00000000 00000000 00000000 01010000 m = n>>3; m = 00000000000 00000000 00000000 01010 案例: n = 80;// 0x50 m = n>>3;// n<>` 数学右移位,高位补充规则 - 正数补0 负数补1,结果满足数学规则 - 如果溢出,向小方向取整 2. `>>>` 是逻辑右移位:高位只补充0 提示:如果数学计算 使用 `>>` 6. << 左移位运算 将2进制数位每位向左移动,右侧填充0 拼接int d1 = 00000000 00000000 00000000 11101010 d2 = 00000000 00000000 00000000 01010001 d3 = 00000000 00000000 00000000 11010111 d4 = 00000000 00000000 00000000 00010100 d4<<24 00010100 00000000 00000000 00000000 d3<<16 00000000 11010111 00000000 00000000 d2<> 数学右移位 面试题目: 如何优化 n*8 ? 答案: n<<3 提示: 被乘数一定是 2 的n次幂 如何优化 n%8 ? 答案: n&(8-1) 如何优化 n%4 ? 答案: n&(4-1) 如何优化 n ? 答案: n&(16-1) n%0xf 提示: 除数一定是 2 的n次幂
### 小数 -1 的原码、反码补码表示 对于小数 `-1`,其在计算机中的原码、反码补码可以通过特定规则来计算。以下是详细的说明: #### 1. 原码表示 原码是最简单的编码方式,其中最高位为符号位(0 表示正数,1 表示负数),剩余部分为数值绝对值的二进制形式。假设字长为 n,则小数 `-1` 的原码可以写成如下形式: - 符号位:由于是负数,因此符号位为 `1`。 - 数值部分:将 `|−1| = 1` 转化为二进制并补齐到指定的小数精度。 例如,在字长为 8 位的情况下,小数 `-1` 的原码可表示为: ```plaintext 1.1000000 ``` 此处第一位为符号位,后续七位为数值部分[^1]。 #### 2. 反码表示 反码基于原码定义而来,当数值为正时,反码等于原码;而当数值为负时,需将原码除符号位外的所有位取反(即 0 变 11 变 0)。按照上述原则,小数 `-1` 的反码可通过以下步骤得出: - 首先写出 `-1` 的原码:`1.1000000`; - 接着对数值部分按位取反得到:`1.0111111`. 最终结果为: ```plaintext 1.0111111 ``` #### 3. 补码表示 补码用于简化加减运算处理过程,尤其适合硬件实现。针对负数而言,其补码是在反码基础上再加一形成。具体操作如下所示: - 已知 `-1` 的反码为 `1.0111111`; - 对该反码加上 `0.0000001`, 结果变为 `1.1000000`. 所以,小数 `-1` 的补码表达式应为: ```plaintext 1.1000000 ``` 值得注意的是,这种模式下零只有一种表现形式,有效避免了重复现象发生[^2]。 --- ### Python 实现代码 下面提供一段简单易懂的Python程序用来验证以上理论推导的结果: ```python def float_to_binary(number, precision=7): """Convert a floating-point number to its binary representation.""" integer_part = int(abs(number)) fractional_part = abs(number) - integer_part # Convert the integer part to binary and remove '0b' bin_integer = bin(integer_part)[2:] # Generate the fractional part as binary string up to given precision bin_fractional = "" while fractional_part > 0 and len(bin_fractional) < precision: fractional_part *= 2 bit = int(fractional_part) if bit == 1: fractional_part -= bit bin_fractional += '1' else: bin_fractional += '0' return f"{bin_integer}.{bin_fractional}" # Example usage for converting -1 with default precision of 7 bits after decimal point. number = -1 binary_representation = float_to_binary(number) print("Original Binary Representation:", binary_representation) original_code = "1." + "".join(['1' if c=='.' else ('0' if c=='1' else '1') for c in binary_representation.split('.')[1]]) print("One's Complement (反码):", original_code.replace('..', '.')) complement_two = list(original_code) if complement_two[-1]=='0': carry=True else: carry=False for i in range(len(complement_two)-1,-1,-1): if not carry or complement_two[i]!='.': break elif complement_two[i]=='0': complement_two[i]='1';carry=False;break; elif complement_two[i]=='1': complement_two[i]='0'; two_complement="".join(complement_two).replace("..",".") print("Two's Complement (补码):", two_complement) ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值