概叙
字节(Byte)是计算机信息技术用于计量存储容量的一种计量单位。
通常情况下,一字节等于八位(bit),即1 Byte = 8 bit。(思考一下:为啥这么规定,单位是字节,且是8比特?)
- 字节是计算机技术中最小的可操作存储单位,通常用于描述存储容量和传输容量。
- 字节是通过网络传输信息或在硬盘或内存中存储信息的单位。
- 字节在计算机系统中扮演着至关重要的角色,主要用于存储和传输数据。
在ASCII码中,一个英文字母(不分大小写)占一个字节的空间,而一个中文汉字通常占两个字节的空间。符号方面,英文标点占一个字节,中文标点占两个字节。
字节与其他存储单位的换算关系
字节与其他存储单位的换算关系如下:
- 1艾字节(EB)= 1024拍字节(PB)
- 1拍字节(PB)= 1024太字节(TB)
- 1太字节(TB)= 1024吉字节(GB)
- 1吉字节(GB)= 1024兆字节(MB)
- 1兆字节(MB)= 1024千字节(KB)
- 1千字节(KB)= 1024字节(Byte)
- 1字节(Byte)= 8比特(bit)
字节的作用
- 存储空间:硬盘、内存等存储设备的容量通常以字节为单位进行计量。例如,硬盘和内存的大小都是以字节为单位来衡量的。数字、字符、图像、音频等各种类型的数据。
- 数据传输:网络带宽、文件大小等也常用字节来描述。例如,HTTP协议中的Content-Length头字段用来指示响应体的长度,单位就是字节。
- 编程与软件开发:在编程中,变量、数组以及其他数据结构的大小往往以字节为单位进行计算。了解字节的概念有助于更有效地管理和优化计算资源。
- 多媒体文件:图片、音频和视频文件的大小通常以字节为单位来衡量。了解不同类型的数据及其所需的存储空间,有助于进行高效的数据管理和优化计算资源。
为什么要熟知字节
- 基础知识的掌握:字节是计算机技术中最小的可操作存储单位,熟知字节对于理解计算机系统的工作原理至关重要。
- 高效的数据处理:在网络通信中,了解和计算传输数据的大小(即多少字节需要被传输)对于优化网络性能、减少延迟和提高数据传输效率至关重要。
- 编程和软件开发:在编程和软件开发中,掌握字节的概念有助于选择合适的数据结构和优化内存使用,从而提高程序的效率和性能。
字节运算/位运算
首先需知道的是,计算机中执行位运算,肯定是采用的补码的方式进行的位计算哦,所以对于真值为负数的情况下,必须先转为补码才能进行计算。下面是java支持的位运算:
- 位与(&):二元运算符,两个为1时结果为1,否则为0
- 位或(|):二元运算符,两个其中有一个为1时结果就为1,否则为0
- 位异或(^):二元运算符,两个数同时为1或0时结果为1,否则为0
- 位取非(~):一元运算符,取反操作
- 左移(<<):一元运算符,按位左移一定的位置。高位溢出,低位补符号位,符号位不变。
- 右移(>>):一元运算符,按位右移一定的位置。高位补符号位,符号位不变,低位溢出。
- 无符号右移(>>>):一元运算符,符号位(即最高位)保留,其它位置向右移动,高位补零,低位溢出。
符号 | 描述 | 运算规则 |
---|---|---|
& | 与 | 两个位都为1时,结果才为1 |
| | 或 | 两个位都为0时,结果才为0 |
^ | 异或 | 两个位相同为0,相异为1 |
~ | 取反 | 0变1,1变0 |
<< | 左移 | 各二进位全部左移若干位,高位丢弃,低位补0 |
>> | 右移 | 各二进位全部右移若干位,对无符号数,高位补0。各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移) |
>>> | 无符号右移 | 各二进位全部右移若干位,对无符号数,高位补0。 |
位运算符结合=赋值操作
- &= 按位与赋值
- |= 按位或赋值
- ^= 按位异或赋值
- >>= 右移赋值
- >>>= 无符号右移赋值
- <<= 赋值左移
这些操作和 “+=” 一个概念。
位运算规则
Java数值运算过程中都是先将十进制转换为二进制然后再进行运算,再把二进制数据转换为十进制展现给用户。二进制运算规则如下:
对于有符号的而言,
- 最高位为符号位,0表示正数,1表示负数
- 正数的原码,反码和补码都一样,三码合一
- 负数的反码:符号位保持不限,其他位取反
- 负数的补码:补码 + 1
- 0的反码和补码都是0
- 计算机的运算的时候,都是将原码转成补码进行运算的
下面以 -1 为例子展示原码、反码和补码的转换关系(以int数据类型为例,int类型在Java中占4字节):
原码、反码和补码是计算机中用于表示整数的三种编码方式。
1.正数的转换
对于正数,原码、反码和补码是相同的。
2.负数的转换
原码转反码:符号位不变,数值位取反。
反码转补码:反码最低位加1。
补码转原码:先减1得到反码,再将数值位取反得到原码。
原码
原码是一种直接表示数值大小和符号的编码方式。
在二进制数中,最高位作为符号位(0表示正数,1表示负数),其余位表示数值的大小。
原码的优点是直观且实现简单,但在进行减法运算时需要将减数的符号位取反,然后再进行加法运算,这使得减法运算相对复杂。
反码
反码用于简化计算机中的减法运算。
- 对于正数,反码与原码相同;
- 对于负数,反码是将原码的数值位取反。
反码的优点是简化了减法运算,负数的反码可以通过加法直接得到。然而,反码也存在正零和负零两种表示,可能会引起混淆。
补码
补码是现代计算机中最常用的整数编码方式。
- 对于正数,补码与原码相同;
- 对于负数,补码是反码的最低位加1。
补码的优点是实现了加法和减法的统一处理,简化了运算过程。
number = 10; // 示例整数
// 计算原码
System.out.println(number+" Original Binary 原码: " + Integer.toBinaryString(number));
// 计算反码(对原码的位取反,但符号位除外)
System.out.println(number+" Reverse Binary 反码: " + Integer.toBinaryString(~number));
// 计算补码(反码加一)
System.out.println(number+" Complement Binary 补码: " + Integer.toBinaryString((~number) + 1));
number = -10; // 示例整数
// 计算原码
System.out.println(number+" Original Binary 原码: " + Integer.toBinaryString(number));
// 计算反码(对原码的位取反,但符号位除外)
System.out.println(number+" Reverse Binary 反码: " + Integer.toBinaryString(~number));
// 计算补码(反码加一)
System.out.println(number+" Complement Binary 补码: " + Integer.toBinaryString((~number) + 1));
输出结果:
10 Original Binary 原码: 1010
10 Reverse Binary 反码: 11111111111111111111111111110101
10 Complement Binary 补码: 11111111111111111111111111110110
-10 Original Binary 原码: 11111111111111111111111111110110
-10 Reverse Binary 反码: 1001
-10 Complement Binary 补码: 1010
字节与字符
字节(Byte)和字符(Character)是计算机科学中两个基本概念,它们之间有着重要的区别:
字节(Byte)
- 定义:字节是计算机存储和处理数据的基本单位,通常由 8 位二进制数字(比特)组成。
- 用途:
- 用于存储数据,如文件、图像、音频等。
- 在内存中,字节用于表示不同类型的数据(整型、浮点型等)。
- 范围:一个字节可以表示 256 (2^8)种不同的值,通常范围为 0 到 255。
- 与编码的关系:在字符编码中,字节用于表示字符。例如,UTF-8 编码的字符可能会占用一个或多个字节。
字符(Character)
- 定义:字符是文本中的基本元素。它可以是字母、数字、符号或空格等。字符是人类可读的文本元素,通常表示一个字母、数字、标点符号或其他符号。字符集定义了一组字符,例如ASCII字符集包含128个字符,而Unicode字符集包含几乎所有世界上的字符。
- 用途:
- 用于文本处理和显示,如字符串、文档等。
- 在编程中,字符通常用单引号表示,例如
'A'
或'1'
。
- 编码:字符通过编码(如 ASCII、UTF-8、UTF-16)转换为字节。例如:
- ASCII 编码将字母 A 表示为一个字节(65)。
- ASCII 编码是最早的字符编码方式,使用7位表示一个字符,因此每个字符占用1个字节(8位)。
- 范围:0-127。
- UTF-8 编码中,汉字可能需要多个字节表示。
- UTF-8 是一种可变长度的字符编码,根据字符的不同而变化字节数。
- 大多数常用的英文字母和标点符号占用1个字节,一些特殊字符和非拉丁字符可能占用2个或更多字节。
- 最大可达4个字节。
-
UTF-16 编码:UTF-16 采用16位(2字节)表示一个字符。常用的字符(基本多文种平面)占用2个字节,一些特殊字符和辅助平面字符占用4个字节(两个16位单元)。最大可达4个字节。
-
UTF-32 编码:UTF-32 使用32位(4字节)表示一个字符。每个字符都占用4个字节。
- ASCII 编码将字母 A 表示为一个字节(65)。
- 范围:字符集(如 Unicode)可以表示多种语言和符号,包括全世界的文字。需要注意的是,对于英文字母和常用符号,UTF-8 编码可能与 ASCII 编码兼容,即一个字符占用1个字节。但对于其他语言的字符、表情符号等,UTF-8 编码可能会占用更多的字节。因此,在处理字符数据时,了解所使用的字符集和编码方式是非常重要的。
总结
- 本质:字节是数据的存储单位,而字符是文本的基本单元。
- 关系:字符通过编码转换为字节,字节则可以组合成字符。
一个字符在计算机中占用的字节数取决于所使用的字符集和编码方式。
不同的字符集和编码方式会为字符分配不同数量的字节。
例如,UTF-8 编码中一个字符通常占用1至4个字节。在 UTF-8 编码中,一个英文字母通常占用一个字节,而一个汉字则可能占用三个字节或更多。因此,在进行文本处理时,需要考虑字符和字节之间的转换,确保编码的一致性,以避免乱码等问题。
位运算
不包含赋值运算,总共7种位运算,接下来我们逐个看过去。
输出结果:
位与(&)
位与(&):二元运算符,两个为1时结果为1,否则为0 。
1 & 1 == 1
1 & 0 == 0
0 & 1 == 0
0 & 0 == 0
与运算的一个用途是检查指定位是否置位(等于1)。
例如一个BYTE里有标识位,要检查第4位是否是1(位图)或者最高位是否是1(判断正负)或者判断最低为是否1(判断奇数和偶数)。
位或(|)
位或(|):二元运算符,两个其中有一个为1时结果就为1,否则为0。
1 | 1 == 1
1 | 0 == 1
0 | 1 == 1
0 | 0 == 0
或运算也可以用来检查置位。例如要检查某个值的第3位是否为0(位图)或者最高位是否是0(判断正负)。
位异或(^)
位异或(^):二元运算符,两个数同时为1或0时结果为1,否则为0。
1 ^ 1 == 0
1 ^ 0 == 1
0 ^ 1 == 1
0 ^ 0 == 0
异或运算可用于位值翻转。例如:(x^n)^n=x,以及利用三次异或完成两个数的交换。
位取非(~)
位取非(~):一元运算符,取反操作。位值取反,置0为1,或置1为0。
非运算的用途是将指定位清0,其余位置1。非运算与数值大小无关。
例如将第1位和第2位清0,其余位置1:
可表达为:
00000011 - 0x03
11111100 - ~0x03 b
0000000000000011 - 0x03
1111111111111100 - ~0x03 w
非运算和与运算结合,可以确保将指定为清0。如将第4位清0:
可表达为:
00110010 - b
& 11101111 - ~0x10
----------
00100010 - result
左移(<<)
左移(<<):一元运算符,按位左移一定的位置。高位溢出,低位补符号位,符号位不变。
将位值向一个方向移动指定的位数。
右移 > > 算子从高位向低位移动,
左移 < < 算子从低位向高位移动。
往往用位移来对齐位的排列(如MAKEWPARAM, HIWORD, LOWORD 宏的功能)。
可表达为:
00001100 - b
00110000 - b < < 2
00000011 - b > > 2
运算规则:将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。
设 a=15,即二进制数00001111,左移2位得00111100,即十进制数60。
左移一位相当于该数乘以2,左移2位相当于该数乘以2^2=4。上面举的例子15<< 2=60,即乘了4。(结论:左移n位等于乘以2的n次方)
设 a=-46,补码后为,1010 1110,a = a<< 2 将a的二进制位左移2位、右补0,即得a=1011 1000,转换为真值后为-56。
设 a=110,补码后为:0110 1110,a = a<<2 将a 的二进制位左移2位,右补0,即得 a=1011 1000,转换为真值后为184。
以此可知,左移n位等于乘以2的n次方,该结论仅适用于该数左移时被溢出舍弃的高位中不包含1的情况,如果溢出的高位中包含1,则不符合上述结论。
右移(>>)
右移(>>):一元运算符,按位右移一定的位置。高位补符号位,符号位不变,低位溢出。
将位值向一个方向移动指定的位数。
右移 > > 算子从高位向低位移动,
左移 < < 算子从低位向高位移动。
往往用位移来对齐位的排列(如MAKEWPARAM, HIWORD, LOWORD 宏的功能)。
可表达为:
00001100 - b
00110000 - b < < 2
00000011 - b > > 2
运算规则:将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。
例如:a=a>>2 将a的二进制位右移2位,左补0 或者 左补1需要看被移数是正还是负。
设 a=16,补码后为00010000,a = a<<2 将a的二进制位右移2位,左边补0,即得a=00000100,转换为真值后为4。
设a=-16,补码后为11110000,a = a<<2 将a的二进制位右移2位,左边补1,得到a=11111100,转换真值后为-4。
结论:右移运算符,操作数每右移一位,相当于该数除以2。
无符号右移(>>>)
无符号右移(>>>):一元运算符,符号位(即最高位)保留,其它位置向右移动,高位补零(无论正负,都是高位补0),低位溢出。
位运算的应用
位运算在编程中的应用主要体现在优化程序和提高运算效率方面。
位运算是一种对整数进行操作的方法,通过对整数在计算机内存中的二进制位进行直接操作,可以实现一些高效的计算。
例如,位运算中的AND、OR、XOR等操作可以用于快速计算某些特定的数学问题,比如判断一个数是否是2的幂、计算两个数的最大公约数等。此外,位运算还可以用于优化程序中的某些特定任务,如快速排序、查找和替换等。
Java中的位运算是一类直接对整数的二进制位进行操作的运算符,包括按位与(&)、按位或(|)、按位异或(^)、按位取反(~)、左移(<<)和右移(>>)。
这些运算符能够高效地执行数值计算和状态控制,特别适用于性能优化、权限控制、数据处理等场景。
位运算的应用场景
- 性能优化:位运算的执行速度快且资源消耗低,常用于性能优化。例如,左移一位相当于乘以2,右移一位相当于除以。
- 权限控制:在权限管理中,可以使用位运算来设置和检查权限。例如,使用按位或操作来设置权限,使用按位与操作来检查权限。
- 数据处理:在处理二进制数据时,位运算可以高效地进行数据压缩、解压和加密解密操作。
- HashMap:在Java集合框架中,HashMap通过复杂的位运算来提高哈希表的均匀分布和查找效率。
位运算的优缺点:
- 优点:位运算通常比加减法运算更快,尤其是在一些古老的微处理器上。虽然在现代架构中,位运算的速度与加法运算相当,但仍快于乘法运算。
- 缺点:位运算只能用于整型和字符型数据,适用范围相对有限。
1.位图
位图(Bitmap或位数组)是一种简单的数据结构,表示一组位,其中每个位可以是 0 或 1。
位图广泛用于计算机系统中,用于内存分配、图像存储和表示集合等任务。
这一种以位为单位存储图像数据的数据结构。它通过将像素数据映射到一系列的位上,实现对图像的编码和解码。
位图在计算机图形学、图像处理、数据压缩等领域有着广泛的应用。
位图的原理是将图像数据映射到一系列的位上,每个位表示一个像素。
每个像素可以由一个或多个位来表示其颜色信息。
例如,一个8位的位图可以表示256种不同的颜色,而一个24位的位图可以表示数百万种颜色。
位图的原理
把X映射的那个标记成1——对应biteset中的set
把X映射的那个标记成0——对应biteset中的reset
判断某位是1还是0——对应biteset中的test
java代码实现位图
输出结果:
2. 实现四则运算
加法:进位和 + 无进位和
- a,b无进位相加,相当于按位异或 a ^ b
- a + b进位信息,相当于按位与之后左移一位(a & b) << 1
- 重复上述步骤,直到进位为0时的 a' ^ b'即为 a + b
减法
- a - b相当于,a加上,b的相反数 a - b -> a + (-b)
- b的相反数为,b按位取反加一,~b + 1
乘法
- a * b 使用二进制计算
- 1.判断(b & 1)是否为 0,不为0则将结果加入
- 2.a 左移一位 a << 1
- 3.b 无符号右移一位 b >>> 1
- 4.当b==0时,返回 res,即为 a * b
除法
a / b = c
- a减去 b << n位后不大于a的数,此时商1
- 不够减商 0
- 重复上述步骤,直到 b << 0位即结果
- b << 可能会越界,我们使用 a >>
例如101100101除以111:
四则运算代码
package com.zxx.study.algorithm.bitmap;
import java.util.Arrays;
/**
* @author zhouxx
* @create 2024-12-27 20:12
*/
public class BitwiseArithmetic {
public static int add(int a, int b) {
if (b == 0) {
return a;
} else {
return add(a ^ b, (a & b) << 1);
}
}
public static int add2(int a, int b) {
int sum = a;
while (b != 0) {
sum = a ^ b;
b = (a & b) << 1;
a = sum;
}
return sum;
}
public static int minus(int a, int b) {
return add(a, add(~b, 1));
}
public static int multiply(int a, int b) {
//将乘数和被乘数都取绝对值
int A = a < 0 ? add(~a, 1) : a;
int B = b < 0 ? add(~b, 1) : b; //计算绝对值的乘积
int P = 0;
while (B != 0) {
if ((B & 1) != 0) { //取乘数的二进制的最后一位,0 or 1
P = add(P, A);
}
A = A << 1;
B = B >> 1;
}
//计算乘积的符号
if ((a ^ b) < 0) {
P = add(~P, 1);
}
return P;
}
//需要指出的是,这种算法在A很大、B很小的情况下效率很低,那该如何优化算法减少while循环的次数呢?
public static int[] divide(int a, int b) { //对被除数和除数取绝对值
int A = a < 0 ? add(~a, 1) : a;
int B = b < 0 ? add(~b, 1) : b;
//对被除数和除数的绝对值求商
int C = A; // 余数C
int N = 0; // 商N
while (C >= B) {
C = minus(C, B); // C-B
N = add(N, 1); // N+1
}
// 求商的符号
if ((a ^ b) < 0) {
N = add(~N, 1);
}
// 求余数的符合
if (a < 0) {
C = add(~C, 1);
}
return new int[]{N, C};
}
/**
* 不难想到,除法是由乘法的过程逆推而来的。例如 9÷4=2...1,也就是2*4+1=9。假设用9去减4*2,可以得出结果等于1,因为1小于4,那么就可以得出9÷4的商是2,余数是1。
* <p>
* 如何确定4的倍数是逼近最终结果的关键。我们知道,int 整型有32位,除首位表示符号位,每一位的大小是 [2^0, 2^1, 2^2, , , 2^30],最大的int整数是2^31-1。
* 所以,我们可以依次将被除数与2^31, 2^30, ...2^3, 2^2, 2^1, 1相乘,如果除数大于它们的乘积,除数就与之相减,并用相减得到的余数继续作为除数,直到循环结束。
*/
public static int[] divide2(int a, int b) {
// 对被除数和除数取绝对值
int A = a < 0 ? add(~a, 1) : a;
int B = b < 0 ? add(~b, 1) : b;
int N = 0; // 商 N
for (int i = 31; i >= 0; i--) {
// 未使用A>=(B<<i)进行判断,因为只有左移B时舍弃的高位不包含1,才相当于该数乘以2的i次方.
if ((A >> i) >= B) {
// A ÷ 2^i >= B
N += (1 << i); // N = N + 2^i
A -= (B << i); // A = A - B*2^i
}
}
int C = A; // 余数C
// 求商的符号
if ((a ^ b) < 0) {
N = add(~N, 1);
} // 求余数的符号
if (a < 0) {
C = add(~C, 1);
}
return new int[]{N, C};
}
public static void main(String[] args) {
int m=1100;
int n=50;
System.out.println("加法:("+m+" + "+n+")=" +(add(m,n)));
System.out.println("加法:("+m+" + "+n+")=" +(add2(m,n)));
System.out.println("减法:("+m+" - "+n+")=" +(minus(m,n)));
System.out.println("乘法:("+m+" * "+n+")=" +(multiply(m,n)));
System.out.println("除法:("+m+" / "+n+")=" +( Arrays.toString(divide(m,n))));
System.out.println("除法:("+m+" / "+n+")=" +( Arrays.toString(divide2(m,n))));
}
}
运行结果:
3.公式:m*2^n = m << n
4.判断奇数和偶数
5.完成字母的大小写转换
6.判断两个数是否异号
7. 加一和减一
8.不用临时变量交换两个数
8.不用临时变量交换数组
9.取绝对值
10.字节数组存大于255的int整数
11.字符转字节(ascii码输出)
12.找出没有重复的数
13.判断数值是否是3^n
14.计算2^n
15.计算x^y
16.使用位运算实现快速乘除法
17.权限判断
位运算在权限控制系统中有着广泛的应用。通过使用位掩码,可以用一个整数来表示多个布尔值,从而实现高效的权限管理。
标签:字符,反码,字节,补码,Java,搞懂,原码,运算 From: https://blog.csdn.net/Rookie_CEO/article/details/144774495