Engee 中的定点算术(固定点
在数值计算领域,绝大多数问题都使用浮点数("Float32"、"Float64")来解决。然而,在实际系统中,如微控制器、DSP、FPGA 或 ASIC,使用浮点类型可能不可取或不可能。例如,在 STM32 系列微控制器中,基本型号没有 "浮点 "硬件支持,浮点运算比整数运算慢得多。在这种情况下,会使用定点运算("Fixed-Point"),小数点数值以整数格式编码,小数部分的长度预先确定。
定点("Fixed-Point")是一种使用普通整数和预定比例(小数部分的位数)表示小数值的方法。它不使用资源密集型浮点运算("Float32"、"Float64"),而是使用一种简单的整数格式,其中 "二进制逗号 "按给定位数移位。
例如,如果小数部分的长度为 ,那么数字 将被存储为整数值 ,因为将内部值(stored_integer
)转换为实数值(real_value
)的公式 将导致 。从公式中可以看出,内部值被存储并等于 ,但在计算过程中会被解释为 。
这种运算具有以下优点:
在文章中阅读有关定点运算的更多信息guide/hdl-fixed-point-arithmetic.adoc#fixed-point-arithmetic
Engee 中的定点算术
为了处理定点运算,Engee 使用了自己的软件包 EngeeFixedPoint.jl
,它取代了标准的 Julia 软件包 FixedPointNumbers.jl
。与经典软件包不同的是,EngeeFixedPoint.jl
提供了高级功能以及对定点数的表示和行为的精确控制—这在资源受限的系统、将计算转移到HDL 以及严格精度的问题中尤为重要。
EngeeFixedPoint.jl "包是一个标准*Engee*包,包含在默认用户环境中,因此无需在代码中明确调用(通过 "import"/"using")。 |
在 Engee 中,定点数的类型如下:
Fixed{S, W, f, T} <: FixedPoint
在哪里?
由于在数据处理的所有阶段都采用了安全键入和明确的行为方式,因此这种格式可让您准确指定数字的存储、解释和参与计算的方式。
为方便起见,``EngeeFixedPoint.jl''提供了多种指定定点类型的方法,从完全手动指定到自动输出。
S, W, f, T = 1, 25, 10, Int32
dt1 = Fixed{S, W, f, T}
dt2 = fixdt(S, W, f)
dt3 = fixdt(Fixed{S, W, f})
dt4 = fixdt(dt2)
println(dt1 == dt2 == dt3 == dt4) # true
在哪里?
-
dt1 = Fixed{S, W, f, T}
- 完整手册描述; -
dt2 = fixdt(S, W, f)
- 简化创建,自动选择类型 ; -
dt3 = fixdt(Fixed{S, W, f})
- 根据现有描述获取类型; -
dt4 = fixdt(dt2)
- 重复使用,从现有类型创建副本。
所有这些选项都会创建相同的类型 Fixed{1, 25, 10, Int32}
,可根据任务使用:
-
当需要控制所有参数时,完整描述(
dt1
)非常有用; -
简化方式 (
dt2
)适用于典型情况,并能缩短代码; -
从类型中获取类型 (
dt3
),在生成代码或键入数据时非常有用; -
重复使用 (
dt4
)有助于处理参数化结构,而无需重新输入参数。
固定 "类型构造函数
接下来,让我们看看在 Engee 中处理定点的具体场景。
例如,可以直接设置类型并传递值:
x = Fixed{1, 15, 2}(25)
结论
fi(6.25, 1, 15, 2)
这意味着 是整数表示(stored_integer
),根据公式 ,实数值(real_value
)将等于 。
固定{S, W, f}(i::T)
。
通过整数表示创建定点的构造函数 Fixed{S, W, f}(i::T)
有以下条件:
-
格式参数:
S
(带符号)、W
(位宽)、f
(小数部分); -
类型为
T
的整数值i
(内部表示)。
S, W, f = 1, 15, 2 # знаковый, 15 бит, 2 бита дробной части
i = 25
x = Fixed{S, W, f}(i) # создание из целого числа
结论
fi(6.25, 1, 15, 2) # эквивалентное представление
固定{S, W, f, T1}(i::T2)`。
与前一个构造函数类似,但可以明确指定存储类型。类型将根据参数 S
、W
、f
自动匹配,与指定的 T1
无关。
T = Int128
x = Fixed{S, W, f, T}(i) # с указанием типа хранения
结论
fi(6.25, 1, 15, 2) # результат идентичен
来自 FixedPointNumbers.jl 的构造函数
尽管使用了新的 EngeeFixedPoint.jl
包,但它仍保留了与 FixedPointNumbers.jl
包的兼容性,以支持许多构造函数。仅支持有符号类型。
支持
-
Fixed{T, f}(i::Integer, _)
- 以整数表示的构造函数。接受类型T
和参数f
; -
Fixed{T, f}(value)
- 以实数(float
)为单位的构造函数。
示例
T = Int32
x1 = Fixed{T, f}(i, nothing) # из целого числа
x2 = Fixed{T, f}(i) # из вещественного числа
结论
6.25 # результат первого конструктора
25.0 # результат второго конструктора
辅助方法 fi
创建定点数的主要便捷方法是使用 fi
辅助方法。与构造函数不同,它们会自动确定表示的参数。
x1 = fi(3.37, 0, 63, 4) # Полный формат с явным указанием параметров
x2 = fi(3.37, fixdt(0, 63, 4)) # Через тип данных
x3 = fi(3.37, 0, 63) # С автоматическим определением дробной части
x4 = fi(100, 1, 8, 5) # Демонстрация обработки переполнения
结论
3.375 # значение с учетом округления
true # x1 и x2 идентичны
3.37 # с автоматическим подбором
3.96875 # результат насыщения при переполнении
使用数组和矩阵
程序库全面支持定点数的矢量和矩阵运算。所有操作都会保留元素类型,并自动将指定精度参数应用于所有数组元素。
向量
创建并处理一维数组。定点参数适用于所有元素:
s, w, f = 1, 62, 7 # знаковый тип, 62 бита, 7 бит дробной части
v = [1, 2, 3] # исходный 向量
# Разные способы создания:
x1 = fi(v, s, w, f) # с явным указанием параметров
x2 = fi(v, fixdt(s, w, f)) # через тип данных
x3 = fi(v, s, w) # с автоматическим определением дробной части
println(x1)
println(x1 == x2)
println(x3)
输出:
Fixed{1, 62, 7}[1.0, 2.0, 3.0]
true
Fixed{1, 62, 59}[1.0, 2.0, 3.0]
复杂矩阵
完全支持多维数组中的复数:
s, w, f = 1, 62, 7
m = [im 2.5; -1.2im 25-im]
# Рабочие способы создания:
x1 = fi(m, s, w, f) # с явным указанием параметров
x2 = fi(m, fixdt(s, w, f)) # через тип данных
println(x1)
println(x1 == x2)
输出
Complex{Fixed{1, 62, 7, Int64}}[fi(0.0, 1, 62, 7) + fi(1.0, 1, 62, 7)*im fi(2.5, 1, 62, 7) + fi(0.0, 1, 62, 7)*im; fi(0.0, 1, 62, 7) - fi(1.203125, 1, 62, 7)*im fi(25.0, 1, 62, 7) - fi(1.0, 1, 62, 7)*im]
true
基本操作和方法
介绍处理定点数的方法,允许您定义允许的数值范围和基本属性。
边界值
通过 typemax
和 typemin
方法,可以定义特定定点类型的最大值和最小值。
dt = fixdt(0, 25, -2) # беззнаковый тип с 25 битами и дробной частью -2
x = fi(1.5, dt) # создаем число фиксированной точки
println(typemax(x)) # 1.34217724e8 – максимальное представимое значение
println(typemin(x)) # 0.0 – минимальное значение для беззнакового типа
数学运算
系统会自动为运算结果选择最佳格式,保持准确性并防止溢出。支持所有基本算术运算(加、减、乘、除):
x1 = fi(1.5, 0, 15, 3)
x2 = fi(1.5, 1, 25, 14)
y1 = x1+x2
y2 = x1-x2
y3 = x1*x2
y4 = x1/x2
println(y1)
println(y2)
println(y3)
println(y4)
println(typeof(y1))
println(typeof(y2))
println(typeof(y3))
println(typeof(y4))
println(x1 == x2)
println(x1 <= x2)
println(x1 > x2)
结论
3.0
0.0
2.25
0.0
Fixed{1, 28, 14, Int32}
Fixed{1, 28, 14, Int32}
Fixed{1, 40, 17, Int64}
Fixed{1, 25, -11, Int32}
true
true
false
四舍五入
通过各种四舍五入策略,您可以控制计算的精确度。默认情况下,使用 RoundNearestTiesUp 四舍五入。
x = fi(1.5, 1, 14, 3) # знаковый, 14 бит, 3 бита дробной части
println(round(x)) # 2.0 – округление к ближайшему целому (1.5 → 2)
println(trunc(x)) # 1.0 – отбрасывание дробной части
println(ceil(x)) # 2.0 – округление вверх к большему целому
println(floor(x)) # 1.0 – округление вниз к меньшему целому
其中
-
round
- 银行四舍五入(四舍五入到最接近的偶数0.5
); -
trunc
- 放弃小数部分; -
ceil
- 总是向上; -
floor
- 始终向下。
类型转换(转换)
在与其他库交互时,转换为标准数据类型非常有用。转换时会考虑四舍五入规则。
x = fi(1.5, 1, 12, 4)
y1 = Int64(x)
y2 = UInt8(x)
y3 = Float64(x)
y4 = convert(fixdt(0, 5, 2), x)
println(y1)
println(y2)
println(y3)
println(y4)
println(typeof(y1))
println(typeof(y2))
println(typeof(y3))
println(typeof(y4))
结论
1
1
1.5
1.5
Int64
UInt8
Float64
Fixed{0, 5, 2, UInt8}
结论
总之,"EngeeFixedPoint.jl "软件包具有以下优点:
-
*扩展的类型系统:
-
完全支持有符号和无符号数;
-
任意位大小(任何位大小,而不仅仅是 8/16/32/64/128);
-
灵活的小数部分设置(包括负值和 ,小数部分长度大于字长的情况 )。
-
-
*改进了类型输出规则:
-
*根据平台生成代码:
-
针对目标平台(C 或 Verilog)的不同类型继承规则;
-
128 位边界溢出时的可预测行为(不同于同类产品)。
-
-
*功能扩展: