进制转换在C/C++/Java/Kotlin中的应用(详细版)(下)

简介: 进制转换在C/C++/Java/Kotlin中的应用(详细版)

进制转换在C/C++/Java/Kotlin中的应用(详细版)(上)+https://developer.aliyun.com/article/1489622


位运算和进制

位运算符的介绍(&,|,^,~,>>,<<)

位运算是指对二进制数的每一位进行逻辑或算术运算。位运算可以提高程序的执行效率,因为它们直接操作计算机的最小单位。在C/C++/Java/Kotlin等编程语言中,可以使用以下的位运算符:

进制位运算主要在二进制上进行,以下是常见的位运算符及其描述:

运算符 名称 描述 示例 (假设 A = 60, B = 13) 结果 (二进制表示)
& 与 (AND) 如果两个相应的二进制位都为1,则结果为1,否则为0 A & B 0000 1100
| 或 (OR) 如果两个相应的二进制位有一个为1,则结果为1,否则为0 A | B 0011 1101
^ 异或 (XOR) 如果两个相应的二进制位一个为0另一个为1,则结果为1,否则为0 A ^ B 0011 0001
~ 非 (NOT) 反转二进制位,0变为1,1变为0 ~A 1100 0011
<< 左移 将二进制数的所有位向左移指定的位数 A << 2 1111 0000
>> 右移 将二进制数的所有位向右移指定的位数 A >> 2 0000 1111
  • &:按位与(bitwise AND),对两个二进制数的每一位进行逻辑与运算,如果两个位都是1,则结果为1,否则为0。例如:
#include <stdio.h>
int main()
{
    int a = 10; // 十进制数10,二进制数1010
    int b = 12; // 十进制数12,二进制数1100
    int c = a & b; // 按位与运算,结果为8,二进制数1000
    printf("%d\n", c); // 输出8
    return 0;
}
  • |:按位或(bitwise OR),对两个二进制数的每一位进行逻辑或运算,如果两个位中有一个是1,则结果为1,否则为0。例如:
#include <stdio.h>
int main()
{
    int a = 10; // 十进制数10,二进制数1010
    int b = 12; // 十进制数12,二进制数1100
    int c = a | b; // 按位或运算,结果为14,二进制数1110
    printf("%d\n", c); // 输出14
    return 0;
}
  • ^:按位异或(bitwise XOR),对两个二进制数的每一位进行逻辑异或运算,如果两个位不同,则结果为1,否则为0。例如:
#include <stdio.h>
int main()
{
    int a = 10; // 十进制数10,二进制数1010
    int b = 12; // 十进制数12,二进制数1100
    int c = a ^ b; // 按位异或运算,结果为6,二进制数0110
    printf("%d\n", c); // 输出6
    return 0;
}
  • ~:按位取反(bitwise NOT),对一个二进制数的每一位进行逻辑非运算,如果该位是1,则结果为0,否则为1。例如:
#include <stdio.h>
int main()
{
    int a = 10; // 十进制数10,二进制数1010
    int b = ~a; // 按位取反运算,结果为-11,二进制数11111111111111111111111111110101(补码表示)
    printf("%d\n", b); // 输出-11
    return 0;
}

注意,在C/C++/Java/Kotlin等编程语言中,整型(int)通常用32位来

  • >>:右移(right shift),将一个二进制数的每一位向右移动指定的位数,左边空出的位用0填充,右边溢出的位舍弃。例如:
#include <stdio.h>
int main()
{
    int a = 10; // 十进制数10,二进制数1010
    int b = a >> 2; // 右移2位,结果为2,二进制数0010
    printf("%d\n", b); // 输出2
    return 0;
}
  • <<:左移(left shift),将一个二进制数的每一位向左移动指定的位数,右边空出的位用0填充,左边溢出的位舍弃。例如:
#include <stdio.h>
int main()
{
    int a = 10; // 十进制数10,二进制数1010
    int b = a << 2; // 左移2位,结果为40,二进制数101000
    printf("%d\n", b); // 输出40
    return 0;
}

注意:在上述示例中,A和B的值是以十进制表示的,但结果是以二进制表示的。例如,A = 60的二进制表示为0011 1100,B = 13的二进制表示为0000 1101。

进制中的位操作不仅仅包括基本的位运算符,工作中还有一些涉及到一些高级概念,如高位、低位、清除某位等。以下是这些概念的详细表格:

概念名称 描述 示例 (假设 A = 60, 即二进制为 0011 1100) 结果 (二进制表示)
高位 (High Bit) 二进制数中的左侧位。在固定长度的二进制数中,高位是最重要的位。 高位(A, 8位) 00
低位 (Low Bit) 二进制数中的右侧位。在固定长度的二进制数中,低位是最不重要的位。 低位(A, 2位) 00
清除某位 使用位运算符将特定位置的位设置为0。 A & ~(1 << 3) (清除第3位) 0001 1100
设置某位 使用位运算符将特定位置的位设置为1。 A | (1 << 3) (设置第3位) 0011 1100
切换某位 使用位运算符切换特定位置的位值。 A ^ (1 << 3) (切换第3位) 0011 0100
检查某位 使用位运算符检查特定位置的位是0还是1。 (A & (1 << 3)) != 0 (检查第3位) True (因为第3位为1)
设置高n位 使用位运算符设置最高的n位为1。 A | ((1 << n) - 1) << (位数 - n) 例如,设置最高2位: 1100 1100
清除高n位 使用位运算符将最高的n位设置为0。 A & ~(((1 << n) - 1) << (位数 - n)) 例如,清除最高2位: 0011 1100
设置低n位 使用位运算符设置最低的n位为1。 A | (1 << n) - 1 例如,设置最低2位: 0011 1101
清除低n位 使用位运算符将最低的n位设置为0。 A & ~((1 << n) - 1) 例如,清除最低2位: 0011 1100
#include <stdio.h>
#include <stdlib.h>
// 将二进制字符串转换为长整型
long binToLong(const char *binStr) {
    return strtol(binStr, NULL, 2);
}
// 将十六进制字符串转换为长整型
long hexToLong(const char *hexStr) {
    return strtol(hexStr, NULL, 16);
}
// 设置高n位为1
long setHighNBits(long num, int totalBits, int n) {
    return num | ((1 << n) - 1) << (totalBits - n);
}
// 清除高n位
long clearHighNBits(long num, int totalBits, int n) {
    return num & ~(((1 << n) - 1) << (totalBits - n));
}
// 设置低n位为1
long setLowNBits(long num, int n) {
    return num | (1 << n) - 1;
}
// 清除低n位
long clearLowNBits(long num, int n) {
    return num & ~((1 << n) - 1);
}
int main() {
    char *s1 = "1010"; 
    char *s2 = "1011";
    char *s3 = "FFFF"; 
    long n1 = binToLong(s1);
    long n2 = binToLong(s2);
    long n3 = n1 & n2;
    long n4 = n1 | n2;
    long n5 = n1 ^ n2;
    long n6 = hexToLong(s3);
  printf("二进制 1010 = %ld\n", n1);  // 10
  printf("二进制 1011 = %ld\n", n2);  // 11
  printf("1010 & 1011 = %ld  (与操作)\n", n3);  // 10
  printf("1010 | 1011 = %ld  (或操作)\n", n4);  // 11
  printf("1010 ^ 1011 = %ld  (异或操作)\n", n5);  // 1
  printf("1011 << 2 = %ld  (左移2位)\n", n2 << 2);  // 44
  printf("1011 >> 2 = %ld  (右移2位)\n", n2 >> 2);  // 2
  printf("65,535 & ~(1 << 3) (清除第3位) = %ld\n", (n6 & ~(1 << 3)));  // 65527
  printf("65527 | (1 << 3) (设置第3位) = %ld\n", (65527 | (1 << 3)));  // 65535
  printf("65527 ^ (1 << 3) (切换第3位) = %ld\n", (65527 ^ (1 << 3)));  // 65535
  printf("65535 ^ (1 << 3) (切换第3位) = %ld\n", (65535 ^ (1 << 3)));  // 65527
  printf("(65535 & (1 << 3)) != 0 (检查第3位) = %d\n", ((65535 & (1 << 3)) != 0));  // 1
  printf("(65527 & (1 << 3)) != 0 (检查第3位) = %d\n", ((65527 & (1 << 3)) != 0));  // 0
  // 高级操作
  printf("设置16383的最高2位为1 = %ld\n", setHighNBits(16383, 16, 2));  // 65535
  printf("清除65535的最高2位 = %ld\n", clearHighNBits(65535, 16, 2));  // 16383
  printf("设置65532的最低2位为1 = %ld\n", setLowNBits(65532, 2));  // 65535
  printf("清除65535的最低2位 = %ld\n", clearLowNBits(65535, 2));  // 65532
    return 0;
}

注意,在C/C++/Java/Kotlin等编程语言中,整型(int)通常用32位来表示,所以左移或右移超过32位的操作是无效的。另外,对于有符号的整型(signed int),右移操作可能会保留符号位(最高位),这称为算术右移(arithmetic right shift)。对于无符号的整型(unsigned int),右移操作不会保留符号位,这称为逻辑右移(logical right shift)。因此,在进行右移操作时,需要注意整型的符号和长度。

位运算在进制转换中的应用

位运算可以用来实现一些简单的进制转换。例如:

  • 将一个十六进制数转换为二进制数:可以将该数分成四个一组的数字,然后将每个数字转换为四位的二进制数,再拼接起来。例如,十六进制数ABC可以分成A,B,C三个数字,然后分别转换为1010,1011,1100三个四位的二进制数,再拼接起来得到101010111100。
  • 将一个二进制数转换为十六进制数:可以将该数分成四个一组的位,然后将每个四位的二进制数转换为一个十六进制数字,再拼接起来。例如,二进制数101010111100可以分成1010,1011,1100三个四位的二进制数,然后分别转换为A,B,C三个十六进制数字,再拼接起来得到ABC。
  • 将一个八进制数转换为二进制数:可以将该数分成三个一组的数字,然后将每个数字转换为三位的二进制数,再拼接起来。例如,八进制数123可以分成1,2,3三个数字,然后分别转换为001,010,011三个三位的二进制数,再拼接起来得到001010011。
  • 将一个二进制数转换为八进制数:可以将该数分成三个一组的位,然后将每个三位的二进制数转换为一个八进制数字,再拼接起来。例如,二进制数001010011可以分成001,010,011三个三位的二进制数,然后分别转换为1,2,3三个八进制数字,再拼接起来得到123。

这些方法都可以用位运算来实现。例如,在C/C++中:

#include <stdio.h>
int main()
{
    int a = 0xABC; // 十六进制数ABC
    int b = 0b101010111100; // 二进制数101010111100
    int c = 0123; // 八进制数123
    int d = 0b001010011; // 二进制数001010011
    // 将十六进制数转换为二进制数
    printf("0x%X = ", a); // 输出0xABC =
    for (int i = 12; i > 0; i -= 4) { // 每次右移4位,取最低4位
        int x = (a >> (i - 4)) & 0xF; // 取最低4位的十六进制数字
        printf("%04b", x); // 输出对应的四位二进制数
    }
    printf("\n"); // 输出换行符
    // 将二进制数转换为十六进制数
    printf("0b%b = ", b); // 输出0b101010111100 =
    for (int i = 12; i > 0; i -= 4) { // 每次右移4位,取最低4位
        int x = (b >> (i - 4)) & 0xF; // 取最低4位的二进制数
        printf("%X", x); // 输出对应的十六进制数字
    }
    printf("\n"); // 输出换行符
    // 将八进制数转换为二进制数
    printf("0%o = ", c); // 输出0123 =
    for (int i = 9; i > 0; i -= 3) { // 每次右移3位,取最低3位
        int x = (c >> (i - 3)) & 0x7; // 取最低3位的八进制数字
        printf("%03b", x); // 输出对应的三位二进制数
    }
    printf("\n"); // 输出换行符
    // 将二进制数转换为八进制数
    printf("0b%b = ", d); // 输出0b001010011 =
    for (int i = 9; i > 0; i -= 3) { // 每次右移3位,取最低3位
        int x = (d >> (i - 3)) & 0x7; // 取最低3位的二进制数
        printf("%o", x); // 输出对应的八进制数字
    }
    printf("\n"); // 输出换行符
    return 0;
}

输出结果为:

0xABC = 101010111100
0b101010111100 = ABC
0123 = 001010011
0b001010011 = 123

进制转换的实际应用

LED控制在嵌入式系统中的应用

在嵌入式系统中,LED灯常用作指示灯,显示系统的状态或者其他信息。为了控制LED灯的亮、灭、闪烁等状态,需要使用位操作来控制硬件寄存器。这是因为硬件寄存器通常是按位来定义的,每一位都有特定的功能。例如,某个寄存器的第0位控制LED1的亮灭,第1位控制LED2的亮灭,以此类推。

#include <stdio.h>
// 假设我们有一个8位的寄存器,用于控制8个LED灯的亮灭
// 我们使用一个字节(8位)来模拟这个寄存器
typedef unsigned char Register;
// LED控制函数
void turnOnLED(Register *reg, int ledNum) {
    *reg |= (1 << ledNum); // 设置指定位为1,亮起LED灯
}
void turnOffLED(Register *reg, int ledNum) {
    *reg &= ~(1 << ledNum); // 清除指定位,熄灭LED灯
}
void toggleLED(Register *reg, int ledNum) {
    *reg ^= (1 << ledNum); // 切换指定位,如果LED灯亮则熄灭,如果熄灭则亮起
}
int isLEDOn(Register reg, int ledNum) {
    return (reg & (1 << ledNum)) != 0; // 检查指定位是否为1
}
int main() {
    Register LEDRegister = 0; // 初始状态,所有LED灯都熄灭
    turnOnLED(&LEDRegister, 2); // 亮起第2个LED灯
    printf("LED 2状态: %s\n", isLEDOn(LEDRegister, 2) ? "亮" : "灭");//LED 2状态: 亮
    toggleLED(&LEDRegister, 2); // 切换第2个LED灯的状态
    printf("LED 2状态: %s\n", isLEDOn(LEDRegister, 2) ? "亮" : "灭");//LED 2状态: 灭
    turnOffLED(&LEDRegister, 2); // 熄灭第2个LED灯
    printf("LED 2状态: %s\n", isLEDOn(LEDRegister, 2) ? "亮" : "灭");//LED 2状态: 灭
    return 0;
}

定义了一个名为Register的类型来模拟硬件寄存器,并使用了位操作来控制LED灯的状态。这种方法在实际的嵌入式系统中非常常见,因为它可以直接操作硬件寄存器,实现对LED灯的精确控制。

解析CAN的标准帧和扩展帧的应用

CAN协议定义了两种帧格式:标准帧和扩展帧。标准帧使用11位的ID,而扩展帧使用29位的ID。为了区分这两种帧格式,CAN协议在帧ID的高位定义了一些标志位

在我写CAN JNI接口实际应用中,需要对CAN帧进行解析,以确定它是标准帧还是扩展帧,并获取其ID。为了实现这一目的,通常需要使用位操作来处理帧ID。

当时很不理解, 上位机发送扩展至0x18121001 为什么native拿到的frame.can_id是0x98121001 , 后面学完位运算符 才理解原来是29位ID 需要清除高3位 才能拿到真正的值。

#include <stdio.h>
#include <stdint.h>
// 假设我们有一个32位的can_id(扩展帧ID),其中高3位用于标志位,低29位用于ID
typedef uint32_t CAN_ID;
// CAN帧结构体
typedef struct {
    CAN_ID can_id;
    // 其他字段,如数据、DLC等
} CAN_Frame;
// 清除标志位
CAN_ID cleanCANID(CAN_ID id) {
    return id & 0x1FFFFFFF; // 清除高3位
}
// 检查是否为扩展帧
int isExtendedFrame(CAN_ID id) {
    return (id & 0x80000000) != 0; // 检查最高位
}
int main() {
    CAN_Frame frame;
    frame.can_id = 0x98121001; // 假设这是一个扩展帧的ID
    printf("原始CAN ID: 0x%X\n", frame.can_id);//原始CAN ID: 0x98121001
    printf("清除标志位后的CAN ID: 0x%X\n", cleanCANID(frame.can_id));//清除标志位后的CAN ID: 0x18121001
    printf("是否为扩展帧: %s\n", isExtendedFrame(frame.can_id) ? "是" : "否");//是否为扩展帧: 是
    return 0;
}

上面的代码定义了一个CAN_Frame结构体来模拟CAN帧,并使用了位操作来处理帧ID。

疑问和解答

问题1: 0x98121001 & 0x1FFFFFFF 和clearHighNBits(0x98121001, 32, 3)有什么区别?

0x98121001 & 0x1FFFFFFF 这个操作是将0x98121001的高3位清零,得到的结果是0x18121001

使用clearHighNBits函数,如果希望清除高3位,那么totalBits应该是32(因为处理的是一个32位的数),n应该是3。函数内部的操作是生成一个掩码,该掩码的高3位是0,其余位是1,然后与输入的数进行与操作。这与上述直接的位操作是相同的。

0x98121001 & 0x1FFFFFFFclearHighNBits(0x98121001, 32, 3) 是等价的。

问题2:(0x98121001 & 0x80000000) != 0;和((0x98121001 & (1 << 31 )) != 0) 有什么区别?

(0x98121001 & 0x80000000) != 0 这个操作是检查0x98121001的最高位是否为1。如果是,则返回true,否则返回false

对于((0x98121001 & (1 << 31)) != 0),这个操作也是检查0x98121001的最高位是否为1。如果是,则返回true,否则返回false

所以(0x98121001 & 0x80000000) != 0((0x98121001 & (1 << 31)) != 0) 是等价的。两者都是检查32位数的最高位。


希望这篇文章能对您有所帮助。如果还有其他问题或建议,请留言与私信。

相关文章
|
14天前
|
人工智能 算法 Java
Java与AI驱动区块链:构建智能合约与去中心化AI应用
区块链技术和人工智能的融合正在开创去中心化智能应用的新纪元。本文深入探讨如何使用Java构建AI驱动的区块链应用,涵盖智能合约开发、去中心化AI模型训练与推理、数据隐私保护以及通证经济激励等核心主题。我们将完整展示从区块链基础集成、智能合约编写、AI模型上链到去中心化应用(DApp)开发的全流程,为构建下一代可信、透明的智能去中心化系统提供完整技术方案。
132 3
|
1月前
|
jenkins Shell 测试技术
|
2月前
|
存储 数据采集 搜索推荐
Java 大视界 -- Java 大数据在智慧文旅旅游景区游客情感分析与服务改进中的应用实践(226)
本篇文章探讨了 Java 大数据在智慧文旅景区中的创新应用,重点分析了如何通过数据采集、情感分析与可视化等技术,挖掘游客情感需求,进而优化景区服务。文章结合实际案例,展示了 Java 在数据处理与智能推荐等方面的强大能力,为文旅行业的智慧化升级提供了可行路径。
Java 大视界 -- Java 大数据在智慧文旅旅游景区游客情感分析与服务改进中的应用实践(226)
|
2月前
|
存储 监控 数据可视化
Java 大视界 -- 基于 Java 的大数据可视化在企业生产运营监控与决策支持中的应用(228)
本文探讨了基于 Java 的大数据可视化技术在企业生产运营监控与决策支持中的关键应用。面对数据爆炸、信息孤岛和实时性不足等挑战,Java 通过高效数据采集、清洗与可视化引擎,助力企业构建实时监控与智能决策系统,显著提升运营效率与竞争力。
|
2月前
|
Java 大数据 数据处理
Java 大视界 -- 基于 Java 的大数据实时数据处理在工业互联网设备协同制造中的应用与挑战(222)
本文探讨了基于 Java 的大数据实时数据处理在工业互联网设备协同制造中的应用与挑战。文章分析了传统制造模式的局限性,介绍了工业互联网带来的机遇,并结合实际案例展示了 Java 在多源数据采集、实时处理及设备协同优化中的关键技术应用。同时,也深入讨论了数据安全、技术架构等挑战及应对策略。
|
2月前
|
数据采集 搜索推荐 Java
Java 大视界 -- Java 大数据在智能教育虚拟学习环境构建与用户体验优化中的应用(221)
本文探讨 Java 大数据在智能教育虚拟学习环境中的应用,涵盖多源数据采集、个性化推荐、实时互动优化等核心技术,结合实际案例分析其在提升学习体验与教学质量中的成效,并展望未来发展方向与技术挑战。
|
7天前
|
Ubuntu API C++
C++标准库、Windows API及Ubuntu API的综合应用
总之,C++标准库、Windows API和Ubuntu API的综合应用是一项挑战性较大的任务,需要开发者具备跨平台编程的深入知识和丰富经验。通过合理的架构设计和有效的工具选择,可以在不同的操作系统平台上高效地开发和部署应用程序。
41 11
|
16天前
|
消息中间件 缓存 Java
Spring框架优化:提高Java应用的性能与适应性
以上方法均旨在综合考虑Java Spring 应该程序设计原则, 数据库交互, 编码实践和系统架构布局等多角度因素, 旨在达到高效稳定运转目标同时也易于未来扩展.
76 8
|
29天前
|
人工智能 Java API
Java与大模型集成实战:构建智能Java应用的新范式
随着大型语言模型(LLM)的API化,将其强大的自然语言处理能力集成到现有Java应用中已成为提升应用智能水平的关键路径。本文旨在为Java开发者提供一份实用的集成指南。我们将深入探讨如何使用Spring Boot 3框架,通过HTTP客户端与OpenAI GPT(或兼容API)进行高效、安全的交互。内容涵盖项目依赖配置、异步非阻塞的API调用、请求与响应的结构化处理、异常管理以及一些面向生产环境的最佳实践,并附带完整的代码示例,助您快速将AI能力融入Java生态。
267 12
|
1月前
|
安全 jenkins Java
Java、Python、C++支持jenkins和SonarQube(一)
Jenkins 是一个开源的 持续集成(CI)和持续交付(CD) 工具,用于自动化构建、测试和部署软件项目。它基于 Java 开发,支持跨平台运行,并拥有丰富的插件生态系统,可以灵活地扩展功能
158 5