// 清除标志位 为了得到正确的can_id,需要在解析之前清除可能设置的标志位。通过使用& 0x1FFFFFFF来实现,这个操作会清除can_id的高3位,确保结果得到的是纯粹的ID。
uint32_t clean_can_id = frame.can_id & 0x1FFFFFFF;
因为上面的问题 我不理解 所以来学习进制转换的应用 , 进制转换非常常见 , 如果你搞底层 这个是必须会的 , 我工作中也经常碰到 每次看到都非常头疼 , 学完就忘 然后大半年碰一次。进制转换不难理解的 , 配合计算器程序员模式 学习起来很快 , 难就难在很难记忆 , 这一次来系统的学习下。
一般用于嵌入式 , Driver 看datasheet 寄存器计算都有可能会碰见。C/C++出现的次数比较多。Java/kotlin网络相关的内容也比较常见。
进制转换在编程中的重要性
进制转换是指将一个数从一种进制表示方式转换 (计算)为另一种进制 (数)表示方式。例如,将十进制数10转换为二进制数1010,或者将十六进制数FF转换为十进制数255。在编程中,经常需要处理不同进制的数,因为计算机内部使用二进制来存储和处理数据,一般使用十进制来输入和输出数据。像我们搞开发的,我们还需要使用八进制或十六进制来表示数据,例如文件权限、寄存器/内存地址、颜色值等。掌握不同进制之间的转换方法,可以帮助我们更好地理解计算机的工作原理。
进制基础
二进制、八进制、十进制和十六进制的概念
不同的表示方法有不同的优点和缺点。二进制最符合计算机的逻辑电路设计,因为它只需要两种状态(开或关)来表示一个位(bit)。但是二进制表示起来比较冗长,不方便阅读和记忆。十进制最符合人脑的计数习惯,因为它与我们的手指数目相对应。但是十进制与计算机的二进制不兼容,需要进行转换。八进制和十六进制是一种折中的方案,它们可以方便地与二进制对应,每个八进制数字对应3个二进制位,每个十六进制数字对应4个二进制位。同时,它们也可以缩短表示长度,提高可读性。
进制名称 | 符号/数字 | 符号/数字数量 | 与二进制的关系 | 优点 | 缺点 |
二进制 (Binary) | 0,1 | 2 | 直接对应 | 符合计算机的逻辑电路设计 | 表示冗长,不方便人类阅读和记忆 |
八进制 (Octal) | 0-7 | 8 | 每数字对应3个二进制位 | 可以方便地与二进制对应,缩短表示长度 | 不如十进制直观,但比二进制更简洁 |
十进制 (Decimal) | 0-9 | 10 | 不直接对应 | 与人类的计数习惯相符,与手指数目相对应 | 与计算机的二进制不兼容,需要转换 |
十六进制 (Hexadecimal) | 0-9, A-F | 16 | 每数字对应4个二进制位 | 可以方便地与二进制对应,缩短表示长度,提高可读性 | 不如十进制直观,但比二进制和八进制更简洁 |
不同进制之间的转换方法
不同进制之间的转换方法有两种:按权展开法和除基取余法。按权展开法是将一个数从一种进制转换为十进制,除基取余法是将一个数从十进制转换为另一种进制。我们以二进制、八进制、十六进制为例,介绍这两种方法。
按权展开法
按权展开法的原理是将一个数按照其所在的位置(称为位权)乘以相应的基数(称为基),然后求和。
进制转换的按权展开法是一个基于数的基数和权重来进行转换的方法。以下是一个简单的表格,展示了从其他进制转换到十进制的按权展开法:
进制 数字 权重 按权展开 二进制 a n a n − 1 … a 1 a 0 2 i a i × 2 i 八进制 b n b n − 1 … b 1 b 0 8 i b i × 8 i 十六进制 c n c n − 1 … c 1 c 0 1 6 i c i × 1 6 i
进制二进制八进制十六进制数字anan−1…a1a0bnbn−1…b1b0cncn−1…c1c0权重2i8i16i按权展开ai×2ibi×8ici×16i进制数字权重按权展开二进制𝑎𝑛𝑎𝑛−1…𝑎1𝑎02𝑖𝑎𝑖×2𝑖八进制𝑏𝑛𝑏𝑛−1…𝑏1𝑏08𝑖𝑏𝑖×8𝑖十六进制𝑐𝑛𝑐𝑛−1…𝑐1𝑐016𝑖𝑐𝑖×16𝑖
进制二进制八进制十六进制数字anan−1…a1a0bnbn−1…b1b0cncn−1…c1c0权重2i8i16i按权展开ai×2ibi×8ici×16i
(i) 是数字的位置,从右到左计数,从0开始。
- 二进制 (1011_2)
进制 数字 权重 按权展开 二进制 1 2 3 1 × 2 3 = 8 二进制 0 2 2 0 × 2 2 = 0 二进制 1 2 1 1 × 2 1 = 2 二进制 1 2 0 1 × 2 0 = 1 总和 8 + 0 + 2 + 1 = 1 1 10
进制二进制二进制二进制二进制数字1011权重23222120总和按权展开1×23=80×22=01×21=21×20=18+0+2+1=1110进制数字权重按权展开二进制1231×23=8二进制0220×22=0二进制1211×21=2二进制1201×20=1总和8+0+2+1=1110
进制二进制二进制二进制二进制数字1011权重23222120总和按权展开1×23=80×22=01×21=21×20=18+0+2+1=1110
- 八进制 (52_8)
进制 数字 权重 按权展开 八进制 5 8 1 5 × 8 1 = 40 八进制 2 8 0 2 × 8 0 = 2 总和 40 + 2 = 4 2 10
进制八进制八进制数字52权重8180总和按权展开5×81=402×80=240+2=4210进制数字权重按权展开八进制5815×81=40八进制2802×80=2总和40+2=4210
进制八进制八进制数字52权重8180总和按权展开5×81=402×80=240+2=4210
- 十六进制 (1A_{16})
进制 数字 权重 按权展开 十六进制 1 1 6 1 1 × 1 6 1 = 16 十六进制 A ( 10 ) 1 6 0 10 × 1 6 0 = 10 总和 16 + 10 = 2 6 10
进制十六进制十六进制数字1A(10)权重161160总和按权展开1×161=1610×160=1016+10=2610进制数字权重按权展开十六进制11611×161=16十六进制𝐴(10)16010×160=10总和16+10=2610
进制十六进制十六进制数字1A(10)权重161160总和按权展开1×161=1610×160=1016+10=2610
注意,在十六进制中,A,B,C,D,E,F分别表示10,11,12,13,14,15。
除基取余法
除基取余法的原理是将一个数不断地除以目标进制的基数,然后将余数倒序排列。例如,十进制数10要转换为二进制,可以不断地除以2,并记录余数。
10 ÷ 2 = 5 余 0 5 ÷ 2 = 2 余 1 2 ÷ 2 = 1 余 0 1 ÷ 2 = 0 余 1
10521÷2=5÷2=2÷2=1÷2=0余余余余010110÷2=5余05÷2=2余12÷2=1余01÷2=0余1
10521÷2=5÷2=2÷2=1÷2=0余余余余0101
然后将余数倒序排列得到:
1 0 10 = 101 0 2 10_{10} =1010_21010=10102
类似地,十进制数83要转换为八进制,可以不断地除以8,并记录余数:
83 ÷ 8 = 10 余 3 10 ÷ 8 = 1 余 2 1 ÷ 8 = 0 余 1
83101÷8=10÷8=1÷8=0余余余32183÷8=10余310÷8=1余21÷8=0余1
83101÷8=10÷8=1÷8=0余余余321
然后将余数倒序排列得到:
8 3 10 = 12 3 8 83_{10} =123_88310=1238
十进制数2748要转换为十六进制,可以不断地除以16,并记录余数:
2748 ÷ 16 = 171 余 12 171 ÷ 16 = 10 余 11 10 ÷ 16 = 0 余 10
274817110÷16=171÷16=10÷16=0余余余1211102748÷16=171余12171÷16=10余1110÷16=0余10
274817110÷16=171÷16=10÷16=0余余余121110
然后将余数倒序排列得到:
274 8 10 = A B C 16 2748_{10} =ABC_{16}274810=ABC16
注意,在十六进制中,10,11,12,13,14,15分别用A,B,C,D,E,F表示。
C/C++/Java/Kotlin中的进制表示
在C/C++/Java/Kotlin等编程语言中,可以使用不同的方式来表示不同进制的数。一般来说,可以使用以下的规则:
- 二进制数:在数前加上
0b
或0B
,表示该数是二进制数。例如,0b1010
表示二进制数1010。 - 八进制数:在数前加上
0
,表示该数是八进制数。例如,0123
表示八进制数123。 - 十进制数:直接写出数,不需要加任何前缀。例如,
10
表示十进制数10。 - 十六进制数:在数前加上
0x
或0X
,表示该数是十六进制数。例如,0xABC
表示十六进制数ABC。
C/C++/Java/Kotlin中进制的转换函数
在C/C++/Java/Kotlin等编程语言中,可以使用一些内置的或自定义的函数来实现不同进制之间的转换。这些函数可以分为两类:字符串转换函数和整数转换函数。
字符串转换函数是指将一个字符串(即一系列字符)按照指定的进制解析为一个整数。例如,将字符串"1010"按照二进制解析为整数10。这类函数通常有两个参数:一个是要转换的字符串,另一个是指定的进制。以下是一些常用的字符串转换函数:
- C/C++中的[
strtol
]函数:该函数可以将一个字符串按照指定的进制(2到36之间)解析为一个长整型(long)数。例如:
#include <stdio.h> #include <stdlib.h> int main() { char *s1 = "1010"; // 二进制字符串 char *s2 = "123"; // 八进制字符串 char *s3 = "ABC"; // 十六进制字符串 long n1 = strtol(s1, NULL, 2); // 将二进制字符串转换为长整型 long n2 = strtol(s2, NULL, 8); // 将八进制字符串转换为长整型 long n3 = strtol(s3, NULL, 16); // 将十六进制字符串转换为长整型 printf("%ld\n", n1); // 输出10 printf("%ld\n", n2); // 输出83 printf("%ld\n", n3); // 输出2748 return 0; }
- Java中的[
Integer.parseInt
]函数:该函数可以将一个字符串按照指定的进制(2到36之间)解析为一个整型(int)数。例如:
public class Main { public static void main(String[] args) { String s1 = "1010"; // 二进制字符串 String s2 = "123"; // 八进制字符串 String s3 = "ABC"; // 十六进制字符串 int n1 = Integer.parseInt(s1, 2); // 将二进制字符串转换为整型 int n2 = Integer.parseInt(s2, 8); // 将八进制字符串转换为整型 int n3 = Integer.parseInt(s3, 16); // 将十六进制字符串转换为整型 System.out.println(n1); // 输出10 System.out.println(n2); // 输出83 System.out.println(n3); // 输出2748 } }
- Kotlin中的[
toInt
]函数:该函数可以将一个字符串按照指定的进制(2到36之间)解析为一个整型(Int)数。例如:
fun main() { val s1 = "1010" // 二进制字符串 val s2 = "123" // 八进制字符串 val s3 = "ABC" // 十六进制字符串 val n1 = s1.toInt(2) // 将二进制字符串转换为整型 val n2 = s2.toInt(8) // 将八进制字符串转换为整型 val n3 = s3.toInt(16) // 将十六进制字符串转换为整型 println(n1) // 输出10 println(n2) // 输出83 println(n3) // 输出2748 }
整数转换函数是指将一个整数按照指定的进制格式化为一个字符串。例如,将整数10按照二进制格式化为字符串"1010"。这类函数通常有两个参数:一个是要转换的整数,另一个是指定的进制。以下是一些常用的整数转换函数:
- C/C++中的[
itoa
]函数:该函数可以将一个整数按照指定的进制(2到36之间)格式化为一个字符串。该函数不是标准库函数,而是一些编译器提供的扩展函数。例如:
#include <stdio.h> #include <stdlib.h> int main() { int n1 = 10; // 十进制数 int n2 = 83; // 十进制数 int n3 = 2748; // 十进制数 char s1[10]; // 存储二进制字符串的数组 char s2[10]; // 存储八进制字符串的数组 char s3[10]; // 存储十六进制字符串的数组 itoa(n1, s1, 2); // 将十进制数转换为二进制字符串 itoa(n2, s2, 8); // 将十进制数转换为八进制字符串 itoa(n3, s3, 16); // 将十进制数转换为十六进制字符串 printf("%s\n", s1); // 输出1010 printf("%s\n", s2); // 输出123 printf("%s\n", s3); // 输出ABC return 0; }
- Java中的[
Integer.toString
]函数:该函数可以将一个整数按照指定的进制(2到36之间)格式化为一个字符串。例如:
public class Main { public static void main(String[] args) { int n1 = 10; // 十进制数 int n2 = 83; // 十进制数 int n3 = 2748; // 十进制数 String s1 = Integer.toString(n1, 2); // 将十进制数转换为二进制字符串 String s2 = Integer.toString(n2, 8); // 将十进制数转换为八进制字符串 String s3 = Integer.toString(n3, 16); // 将十进制数转换为十六进制字符串 System.out.println(s1); // 输出1010 System.out.println(s2); // 输出123 System.out.println(s3); // 输出ABC
- Kotlin中的[
toString
]函数:该函数可以将一个整数按照指定的进制(2到36之间)格式化为一个字符串。例如:
fun main() { val n1 = 10 // 十进制数 val n2 = 83 // 十进制数 val n3 = 2748 // 十进制数 val s1 = n1.toString(2) // 将十进制数转换为二进制字符串 val s2 = n2.toString(8) // 将十进制数转换为八进制字符串 val s3 = n3.toString(16) // 将十进制数转换为十六进制字符串 println(s1) // 输出1010 println(s2) // 输出123 println(s3) // 输出ABC }
进制转换在C/C++/Java/Kotlin中的应用(详细版)(下)+https://developer.aliyun.com/article/1489623