浮点数的存储方式、bf16和fp16的区别

news2024/10/6 10:39:41

目录

  • 1. 小数的二进制转换
  • 2. 浮点数的二进制转换
  • 3. 浮点数的存储
    • 3.1 以fp32为例
    • 3.2 规约形式与非规约形式
  • 4. 各种类型的浮点数
  • 5. BF16和FP16的区别
  • Ref

1. 小数的二进制转换

十进制小数转换成二进制小数采用「乘2取整,顺序排列」法。具体做法是:用 2 2 2 乘十进制小数,可以得到积,将积的整数部分取出,再用 2 2 2 乘余下的小数部分,又得到一个积,再将积的整数部分取出,如此进行,直到积中的小数部分为零,或者达到所要求的精度为止。

然后把取出的整数部分按顺序排列起来,先取的整数作为二进制小数的高位有效位,后取的整数作为低位有效位。

二进制小数转十进制小数则十分简单。我们知道二进制表示整数时,最低位代表 2 2 2 0 0 0 次方,往高位依次是 2 2 2 1 1 1 次方, 2 2 2 次方, 3 3 3 次方。那么对应的,二进制数小数点后面,最高位则是 2 2 2 − 1 -1 1 次方,如下图所示:

因此: ( 0.1101 ) 2 = 1 ⋅ 2 − 1 + 1 ⋅ 2 − 2 + 0 ⋅ 2 − 3 + 1 ⋅ 2 − 4 = ( 0.8125 ) 10 (0.1101)_2=1\cdot2^{-1}+1\cdot2^{-2}+0\cdot2^{-3}+1\cdot2^{-4}=(0.8125)_{10} (0.1101)2=121+122+023+124=(0.8125)10

整数可以使用 2 2 2非负幂次的有限和来表示,但小数就不一定能够用 2 2 2幂次的有限和来表示了,部分情况下可能是无限和。

例如对于小数 0.6 0.6 0.6 而言,它的二进制形式为 0. 1001 ‾ 0.\overline{1001} 0.1001,是一个无限循环小数,循环节是 1001 1001 1001

计算机只能识别 0 0 0 1 1 1,这就是为什么我们需要讨论十进制小数到二进制小数的转换。注意到在上述的例子中, 0.6 0.6 0.6 转换成二进制后是一个无限循环小数,由于计算机内存有限,无法存储无限长的数字序列,因此在实际存储这类数字时,只能存储小数点后的有限位数。这就意味着,无论如何,存储时都必须进行截断或四舍五入处理,而这种操作会引入所谓的「浮点数精度误差」。

很显然,对于这类小数,能够存储的位数越多,相应的精度就越高。这就是为什么 double 类型相比于 float 类型能提供更高精度的原因。

⚠️ 浮点数存储遵循IEEE 754规范。

为加深理解,我们使用Python来实现一遍:

def decimal_to_binary(decimal, max_step=30):
    binary = '0.'
    cnt = 0

    while decimal != 0 and cnt < max_step:
        decimal *= 2
        bit = int(decimal)
        binary += str(bit)
        decimal -= bit
        cnt += 1

    return binary

print(decimal_to_binary(0.8125))
print(decimal_to_binary(0.6))

2. 浮点数的二进制转换

粗略地讲,一个浮点数由其整数部分小数部分组成,在知道小数的进制转换后,我们可以将一个十进制浮点数转换成二进制形式。

10.625 10.625 10.625 为例。只需注意到

10.625 = 10 + 0.625 = 8 + 2 + 0.5 + 0.125 = 2 3 + 2 1 + 2 − 1 + 2 − 3 \begin{aligned} 10.625&=10+0.625=8+2+0.5+0.125 \\ &=2^3+2^1+2^{-1}+2^{-3} \end{aligned} 10.625=10+0.625=8+2+0.5+0.125=23+21+21+23

于是 ( 10.625 ) 10 = ( 1010.101 ) 2 (10.625)_{10}=(1010.101)_2 (10.625)10=(1010.101)2。我们还可以进一步将其表示成 2 2 2 为底的科学记数法 1010.101 = 1.010101 × 2 3 1010.101=1.010101\times 2^3 1010.101=1.010101×23

一些其他的例子:

3.5 → 1.11 × 2 1 0.8125 → 1.101 × 2 − 1 − 0.6 → − 1. 0011 ‾ × 2 − 1 \begin{aligned} 3.5 &\to 1.11\times 2^1 \\ 0.8125 &\to 1.101\times 2^{-1} \\ -0.6&\to -1.\overline{0011}\times 2^{-1} \\ \end{aligned} 3.50.81250.61.11×211.101×211.0011×21

由此可见,任何(正)十进制浮点数表示成二进制科学记数法以后,一定是 1 1 1 点几(尾数)乘以 2 2 2 的多少次方(指数)。对于负数来说,就是负 1 1 1 点几(尾数)乘以 2 2 2 的多少次方(指数)。因此,任意一个二进制浮点数都可以表示成下面的形式:

V = ( − 1 ) s × M × 2 E (1) V=(-1)^s\times M\times 2^E\tag{1} V=(1)s×M×2E(1)

其中 s s s 代表符号位(sign), s = 1 s=1 s=1 说明是负数, s = 0 s=0 s=0 说明是正数。 M M M尾数(mantissa), E E E指数(exponent)。因此存储一个浮点数只需要存储 s , M , E s,M,E s,M,E 这三部分。


一些发现:

  • 尾数 M M M最高位总是 1 1 1,因此实际存储时,只需要存储尾数的小数部分(fraction),即小数点后面的数字。
  • M ∈ [ 1 , 2 ) M\in[1, 2) M[1,2)。这是因为, M M M 最高位是 1 1 1,所以至少是 1.000... 1.000... 1.000...,极端情况下是 1.111... 1.111... 1.111...,即 1 + 1 2 + 1 4 + . . . 1+\frac12+\frac14+... 1+21+41+...,即使尾数达到了 2 2 2,即 10.000... 10.000... 10.000...,也会被重新规范化到 1.000... 1.000... 1.000...,即 1 1 1
  • E ∈ Z E\in\mathbb{Z} EZ,可正可负可为 0 0 0
  • 注意到 M × 2 − 1 ∈ [ 0.5 , 1 ) ,    M × 2 1 ∈ [ 2 , 4 ) M\times 2^{-1}\in[0.5,1),\;M\times 2^1\in[2,4) M×21[0.5,1),M×21[2,4)。因此,在十进制下,任何小于 1 1 1 的数转化成二进制后,其指数一定小于 0 0 0;任何大于等于 2 2 2 的数转化成二进制后,其指数一定大于 0 0 0。进一步可知,对于任意非零十进制浮点数 x x x,将其转化成二进制后,其指数为 ⌊ log ⁡ 2 ∣ x ∣ ⌋ \lfloor \log_2|x|\rfloor log2x
  • 只考虑正浮点数(负浮点数亦然)。由上一条知,所有指数将所有的正浮点数 ( 0 , + ∞ ) (0,+\infty) (0,+) 划分成若干个互不相交的区间: ( 0 , + ∞ ) = ⋃ k = − ∞ + ∞ [ 2 k , 2 k + 1 ) (0,+\infty)=\bigcup_{k=-\infty}^{+\infty}[2^k,2^{k+1}) (0,+)=k=+[2k,2k+1)。设有两个符号相同的十进制浮点数 x 1 , x 2 x_1,x_2 x1,x2,它们在二进制下的指数分别为 k 1 , k 2 k_1,k_2 k1,k2,且 k 1 > k 2 k_1>k_2 k1>k2。若 x 1 , x 2 x_1,x_2 x1,x2 均为正,则 x 1 > x 2 x_1>x_2 x1>x2;若 x 1 , x 2 x_1,x_2 x1,x2 均为负,则 x 1 < x 2 x_1<x_2 x1<x2。这说明我们可以仅靠指数来比较两个浮点数的大小(前提是指数不同)。
  • M M M 的位数通常决定了浮点数能表示的精度。例如对于十进制小数 0.6 0.6 0.6 而言,将其转化成二进制后,能存储的尾数位越多,表达自然越精确。 E E E 的位数则决定了浮点数能够表示的数值范围

3. 浮点数的存储

3.1 以fp32为例

IEEE 754规定的浮点数存储格式如下:

最高位是符号位, 1 1 1 代表负数, 0 0 0 代表正数。接下来的若干位是指数位,指数位之后才是尾数位(不存储最高位 1 1 1)。

以fp32(32-bit floating point,32位浮点数)为例,它拥有 1 1 1 位符号位、 8 8 8 位指数位, 23 23 23 位尾数位。

8 8 8 位有符号整数的取值范围是 [ − 128 , 127 ] [-128,127] [128,127],可以表示 256 256 256 个指数。但实际上,我们前面说过,指数不等时可以仅通过指数来比较两个浮点数的大小。为了简化这个比较手续,考虑采用 8 8 8无符号整数进行存储,这样就能仅通过字典序来比较两个指数的大小了。在该设定下,指数的取值范围变成了 [ 0 , 255 ] [0,255] [0,255]

浮点数中还有三个特殊值 0 , ∞ , NaN 0,\infty,\text{NaN} 0,,NaN。IEEE 754规定,需要留一些位置用来表示这些特殊值: 0 0 0 用来留给 0 0 0 255 255 255 用来留给 ∞ , NaN \infty,\text{NaN} ,NaN。挖掉两个位置后,指数的取值范围变成了 [ 1 , 254 ] [1,254] [1,254]。注意到此时还没有负指数,为了平衡正负指数的个数,这里引入指数偏移量 c = 127 c=127 c=127,于是有

存储指数 − 127 = 实际指数 存储指数-127=实际指数 存储指数127=实际指数

故知实际指数的范围为 [ − 126 , 127 ] [-126,127] [126,127]

🧑‍💻 一般地,设 e e e 为指数位的个数,则 c ≜ 2 e − 1 − 1 c\triangleq 2^{e-1}-1 c2e11。fp32的指数偏移量为 2 8 − 1 − 1 = 127 2^{8-1}-1=127 2811=127

例如, 10.625 10.625 10.625 转化成二进制后指数为 3 3 3,那么用来存储的指数值为 127 + 3 = 130 127+3=130 127+3=130,即 10000010 10000010 10000010。尾数部分只需要存储 010101 010101 010101,因此 10.625 10.625 10.625 用fp32表示即为

0 ⏟ 1 个符号位    10000010 ⏟ 8 个指数位    01010100000000000000000 ⏟ 23 个尾数位 \underbrace{0}_{1个符号位}\;\underbrace{10000010}_{8个指数位}\;\underbrace{01010100000000000000000}_{23个尾数位} 1个符号位 08个指数位 1000001023个尾数位 01010100000000000000000

那反过来该如何操作呢?例如,给定一个fp32格式的浮点数 11000001010011000000000000000000,首先分离出它的符号位、指数位和尾数位:

  • 符号位: 1 1 1
  • 指数位: 10000010 10000010 10000010
  • 尾数位: 10011000000000000000000 10011000000000000000000 10011000000000000000000

由符号位可知这是一个负数,将指数位转换成十进制得到 130 130 130,因此实际指数为 130 − 127 = 3 130-127=3 130127=3。由此可知该浮点数的二进制科学记数法表示为 − 1.10011 × 2 3 - 1.10011\times2^3 1.10011×23,从而

− 1.10011 × 2 3 = − 1100.11 = − ( 2 3 + 2 2 + 2 − 1 + 2 − 2 ) = − 12.75 -1.10011\times2^3=-1100.11=-(2^3+2^2+2^{-1}+2^{-2})=-12.75 1.10011×23=1100.11=(23+22+21+22)=12.75

我们还可以用Python来完成这个互相转化的操作:

import struct

def binary_to_float(binary_str):
    assert len(binary_str) == 32, "only support fp32"

    # 4 bytes, 32 bits.
    decimal_float = int(binary_str, 2).to_bytes(4, 'big')
    return struct.unpack('>f', decimal_float)[0]


def float_to_binary(value):
    packed_float = struct.pack('>f', value)
    binary_str = ''.join(f'{byte:08b}' for byte in packed_float)
    return binary_str


print(binary_to_float('11000001010011000000000000000000'))
print(float_to_binary(-12.75))

3.2 规约形式与非规约形式

回到 ( 1 ) (1) (1) 式,已知 M ∈ [ 1 , 2 ) M\in[1,2) M[1,2),设有 e e e 个指数位,那么存储指数 E ∈ [ 0 , 2 e − 1 ] E\in[0,2^e-1] E[0,2e1]。当 E ∈ [ 1 , 2 e − 2 ] E\in[1, 2^e-2] E[1,2e2] 时,此时 ( 1 ) (1) (1) 式又称规约形式

还是以fp32为例,只考虑正数,在规约形式下

  • 最小值: M = 1 ,   E = 1 − 127 = − 126 M=1,\,E=1-127=-126 M=1,E=1127=126,故 V = 2 − 126 ≈ 1.18 × 1 0 − 38 V=2^{-126}\approx1.18\times10^{-38} V=21261.18×1038
  • 最大值: M = 1 + 2 − 1 + ⋯ + 2 − 23 = 2 − 2 − 23 ,   E = 254 − 127 = 127 M=1+2^{-1}+\cdots+2^{-23}=2-2^{-23},\,E=254-127=127 M=1+21++223=2223,E=254127=127,故 V = ( 2 − 2 − 23 ) ⋅ 2 127 ≈ 3.40 × 1 0 38 V=(2-2^{-23})\cdot2^{127}\approx3.40\times 10^{38} V=(2223)21273.40×1038

很显然,fp32无法表示低于最小值的数,低于最小值的数会被视为 0 0 0(下溢)。同样地,高于最大值的数会被视为 ∞ \infty (上溢)。

更具体地:

  • M = 0 , E = 0 M=0,E=0 M=0,E=0 来表示 0 0 0
  • M = 0 , E = 2 e − 1 M=0,E=2^e-1 M=0,E=2e1 来表示 ∞ \infty
  • M ≠ 0 , E = 2 e − 1 M\neq 0,E=2^e-1 M=0,E=2e1 来表示 NaN \text{NaN} NaN

为了让fp32能表示低于最小值的数(即更接近0的数),并且让从最小规约数向 0 0 0 的过渡更加平滑,非规约数便由此引入。这种设计使得浮点数系统能够更细致地处理接近零的小数值。

我们先思考一下,如何让fp32来表示小于 2 − 126 2^{-126} 2126 的数呢?显然存储指数 E E E 已经达到了最小值 1 1 1,而 M ∈ [ 1 , 2 ) M\in[1,2) M[1,2),将 M M M 的隐含最高位由 1 1 1 改为 0 0 0,从而 M ∈ [ 0 , 1 ) M\in[0,1) M[0,1),我们便可以表示比最小规约数还要小的数。

IEEE 754规定,非规约数的实际存储指数为 0 0 0,而计算公式则为 1 − c 1-c 1c,其中 c c c 为指数偏移量。并且 M ∈ ( 0 , 1 ) M\in(0,1) M(0,1)。当 E = 0 , M ≠ 0 E=0,M\neq0 E=0,M=0 时,则按照非规约数来解析fp32。

我们来计算一下非规约形式下的最小值和最大值:

  • 最小值: M = 2 − 23 , E = 0 M=2^{-23},E=0 M=223,E=0 V = 2 − 23 ⋅ 2 − 126 = 2 − 149 ≈ 1.4 × 1 0 − 45 V=2^{-23}\cdot 2^{-126}=2^{-149}\approx 1.4\times 10^{-45} V=2232126=21491.4×1045
  • 最大值: M = 2 − 1 + 2 − 2 + ⋯ + 2 − 23 = 1 − 2 − 23 , E = 0 M=2^{-1}+2^{-2}+\cdots+2^{-23}=1-2^{-23},E=0 M=21+22++223=1223,E=0 V = ( 1 − 2 − 23 ) ⋅ 2 − 126 ≈ 2 − 126 V=(1-2^{-23})\cdot 2^{-126}\approx2^{-126} V=(1223)21262126

我们还可以进一步计算得到一般形式下的一些结论。设一个浮点数有 e e e 个指数位和 f f f 个尾数位,令 c ≜ 2 e − 1 − 1 c\triangleq 2^{e-1}-1 c2e11,则:

  • 规约形式下的最小值: 2 1 − c 2^{1-c} 21c
  • 规约形式下的最大值: ( 2 − 2 − f ) ⋅ 2 c (2-2^{-f})\cdot 2^{c} (22f)2c
  • 非规约形式下的最小值: 2 1 − c − f 2^{1-c-f} 21cf
  • 非规约形式下的最大值: ( 1 − 2 − f ) ⋅ 2 1 − c (1-2^{-f})\cdot 2^{1-c} (12f)21c

可以看出有以下等式成立:

非规约形式下的最小值 + 非规约形式下的最大值 = 规约形式下的最小值 非规约形式下的最小值+非规约形式下的最大值=规约形式下的最小值 非规约形式下的最小值+非规约形式下的最大值=规约形式下的最小值

关于特殊值的一些结论:

E E E M M M形式
[ 1 , 2 e − 2 ] [1,2^e-2] [1,2e2] [ 1 , 2 ) [1,2) [1,2)规约形式
0 0 0 ( 0 , 1 ) (0,1) (0,1)非规约形式
0 0 0 0 0 0
2 e − 1 2^e-1 2e1 0 0 0无穷
2 e − 1 2^e-1 2e1 ≠ 0 \neq0 =0NaN

4. 各种类型的浮点数

名称指数位尾数位
FP16(半精度浮点数) 5 5 5 10 10 10
BF16(Brain Floating) 8 8 8 7 7 7
FP32(单精度浮点数) 8 8 8 23 23 23
FP64(双精度浮点数) 11 11 11 52 52 52
FP128(四精度浮点数) 15 15 15 112 112 112

5. BF16和FP16的区别

先使用之前得到的公式计算一下规约形式下的最小值和最大值:

类型MinMax
FP16 2 − 14 ≈ 6.10 × 1 0 − 5 2^{-14}\approx 6.10\times10^{-5} 2146.10×105 ( 2 − 2 − 10 ) ⋅ 2 15 = 65504 (2-2^{-10})\cdot 2^{15}=65504 (2210)215=65504
BF16 2 − 126 ≈ 1.18 × 1 0 − 38 2^{-126}\approx1.18\times10^{-38} 21261.18×1038 ( 2 − 2 − 7 ) ⋅ 2 127 ≈ 3.39 × 1 0 38 (2-2^{-7})\cdot2^{127}\approx3.39\times10^{38} (227)21273.39×1038
  • BF16和FP32的指数位相同,像是从FP32中截取了前16位一样。也正是因为指数位相同,使得BF16的数值范围与FP32相似
  • BF16相比FP16的数值范围更大,但是精度会更低(深度学习中,数值范围的重要性是远大于数值精度的)。
  • FP16的数值范围过小,容易造成溢出。
  • FP16更多用于需要较高精度但数值范围要求不是特别严格的场合,如某些图形处理和较轻量级的神经网络模型推理。

Ref

[1] https://blog.csdn.net/u013250861/article/details/131152163
[2] https://www.paddlepaddle.org.cn/documentation/docs/zh/dev_guides/amp_precision/amp_op_dev_guide_cn.html
[3] https://cloud.tencent.com/developer/article/1473541
[4] https://www.runoob.com/w3cnote/decimal-decimals-are-converted-to-binary-fractions.html
[5] https://leokongwq.github.io/2017/08/19/computer-how-float-stored.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1602281.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何协调数据集成和数据质量?

想象一下用腐烂的木头制作的一件漂亮的家具或用劣质面料制成的高级时尚衬衫。材料的质量影响最终产品。那么&#xff0c;为什么数据洞察&#xff08;贵公司庞大的数据管理工作的主要产品&#xff09;会有所不同呢&#xff1f; 无论您的数据管理生态系统有多强大&#xff0c;或…

《自动机理论、语言和计算导论》阅读笔记:p139-p171

《自动机理论、语言和计算导论》学习第 7 天&#xff0c;p139-p171总结&#xff0c;总计 33 页。 一、技术总结 1.reversal p139, The reversal of a string a1a2…an is the string written backwards, that is anan-1…a1. 2.homomorphism A string homomorphism is a f…

点云语义分割:使用Cylinder3D训练SemanticKITTI数据集

点云语义分割:使用Cylinder3D训练SemanticKITTI数据集 一、环境二、数据准备3、训练4、测试5、可视化 一、环境 系统&#xff1a;Ubuntu18 Pytorch&#xff1a;1.5.0 GPU&#xff1a;Tesla V100 cuda&#xff1a;10.2 代码: Cylinder3D 二、数据准备 下载semanticKITTI数据集…

32.5k star!发现一个新的 API 调试工具!postman 要被替换了【文末有项目源码】

在软件开发过程中&#xff0c;API&#xff08;应用程序接口&#xff09;扮演着至关重要的角色。为了确保 API 的可靠性和性能&#xff0c;开发人员需要一种高效的方式来测试和调试它们。这方面的工具&#xff0c;大家经常用到的应该就是 postman 了。不过&#xff0c;今天想要给…

钡铼IOy系列模块深挖工业场景需求提供丰富多样的I/O解决方案

钡铼IOy系列模块以其灵活性和多样性&#xff0c;在工业场景中提供了丰富多样的I/O解决方案&#xff0c;满足了不同行业、不同应用场景的需求。以下是一些常见的工业场景需求及钡铼IOy系列模块提供的解决方案&#xff1a; 1. 工厂自动化 需求&#xff1a;工厂自动化需要对生产线…

03-JAVA设计模式-迭代器模式

迭代器模式 什么是迭代器模式 迭代器模式&#xff08;demo1.Iterator Pattern&#xff09;是Java中一种常用的设计模式&#xff0c;它提供了一种顺序访问一个聚合对象中各个元素&#xff0c;而又不需要暴露该对象的内部表示的方法。迭代器模式将遍历逻辑从聚合对象中分离出来…

斯坦福大学2024年人工智能发展和前景全面分析报告

2024 年指数是斯坦福大学迄今为止最全面的指数&#xff0c;恰逢人工智能对社会的影响力达到前所未有的重要时刻。今年&#xff0c;斯坦福大学扩大了研究范围&#xff0c;更广泛地涵盖人工智能的技术进步、公众对该技术的看法以及围绕其发展的地缘政治动态等基本趋势。 完整详细…

C++基本输入输出

C 中的输入和输出&#xff08; I/O &#xff09;主要是通过标准库中的输入输出流来实现的。最常用的是 iostream 1. 库&#xff0c;它提供了用于输入和输出的基本流类&#xff0c;包括 cin 、 cout 、 cerr 和 clog 。 1.标准输出流 ( cout ) cout 代表标准输出流&a…

Java 基于微信小程序的医院预约挂号小程序(V3)

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

短信防刷之滑动验证码

前言&#xff1a;最近想写一个滑动验证码&#xff0c;前台的样式虽然很好看&#xff0c;但是并不安全&#xff0c;网上也都是一些demo&#xff0c;不是前后台分离的&#xff0c;然后就自己查资料&#xff0c;自己来完成了 滑动验证码 一、为什么要使用滑动验证码 首先&#x…

C++ 秋招必知必会(数据结构与算法:下)

20. 二叉树的定义与操作 二叉树&#xff08;binary tree&#xff09;是一种非线性数据结构&#xff0c;代表着祖先与后代之间的派生关系&#xff0c;体现着“一分为二”的分治逻辑 与链表类似&#xff0c;二叉树的基本单元是节点&#xff0c;每个节点包含&#xff1a;值、左子…

吐血整理102个Python项目,从基础到高级,练完你就牛了!

前言 Python 初学者在迈过安装编程环境和基本语法的门槛&#xff0c;准备大展身手的时候&#xff0c;可能突然就会进入迷茫期&#xff1a; 不知道做些什么、再学些什么。。。 然后对编程的兴趣就会慢慢消退&#xff0c;找不到坚持下去的理由&#xff0c;从而慢慢淡忘之前学会…

JookDB下载安装使用

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

【Arduino IDE 环境配置】

目录 Arduino IDE 环境配置 1. 安装方式2. 操作方法&#xff08;Arduino中文社区&#xff09; 2.1. 安装Arduino IDE2.2. 下载固件2.3. 修改Arduino IDE语言2.4. 添加开发板管理网址2.5. 运行离线包2.6. 检查安装是否成功 下载Arduino IDE&#xff1a; 如果你还没有安装Arduin…

文件包含漏洞利用技术总结

开发人员一般会把重复使用的函数写到单个文件中&#xff0c;需要使用某个函数时直接调用此文件&#xff0c;而无需再次编写&#xff0c;这中文件调用的过程一般被称为文件包含。 allow_url_fopen On&#xff08;是否允许打开远程文件&#xff09; allow_url_include On&…

claude3国内能用吗

claude3国内能用吗 如果您在国内无法直接使用Claude模型&#xff0c;可以考虑以下几种解决办法&#xff1a; 镜像站点&#xff1a;和GPT模型相似&#xff0c;使用为国内用户设置的镜像网站可以是一个解决方案。这些镜像站点可能会提供Claude模型的本地化服务&#xff0c;确保…

CAPL 定时器数组 实现同时注入多条CAN报文

&#x1f345; 我是蚂蚁小兵&#xff0c;专注于车载诊断领域&#xff0c;尤其擅长于对CANoe工具的使用&#x1f345; 寻找组织 &#xff0c;答疑解惑&#xff0c;摸鱼聊天&#xff0c;博客源码&#xff0c;点击加入&#x1f449;【相亲相爱一家人】&#x1f345; 玩转CANoe&…

HTTP1.0、HTTP1.1、HTTP2.0、HTTP3.0傻傻分不清楚

一、HTTP1.0 默认使用短连接。无状态&#xff0c;无连接。 每个请求都需要新建TCP连接&#xff0c;性能较低。 不支持多路复用。 基于文本的协议。 不支持头部压缩。 请求头不支持Host头域。 不支持服务端推送。 不支持请求优先级。 不允许断点续传。 默认不加密&#xff0c;可…

【一刷《剑指Offer》】面试题 4:替换空格

力扣对应链接&#xff1a;LCR 122. 路径加密 - 力扣&#xff08;LeetCode&#xff09; 牛客对应链接&#xff1a;替换空格_牛客题霸_牛客网 (nowcoder.com) 核心考点 &#xff1a;字符串相关&#xff0c;特性观察&#xff0c;临界条件处理。 一、《剑指Offer》内容 二、分析问…

AlgorithmDay14

day14 二叉树基础 二叉树的种类 满二叉树 只有度为0和2的结点&#xff0c;并且度为0的结点在同一层 &#xff08;深度为k 有2^k-1个结点&#xff09; 完全二叉树 除了最底层可能每天&#xff0c;其余都填满了&#xff0c; 并且最底层的结点集中在该层的左边位置。 二叉…