Engee 文档

Engee 中的定点算术(固定点

在数值计算领域,绝大多数问题都使用浮点数("Float32"、"Float64")来解决。然而,在实际系统中,如微控制器、DSP、FPGA 或 ASIC,使用浮点类型可能不可取或不可能。例如,在 STM32 系列微控制器中,基本型号没有 "浮点 "硬件支持,浮点运算比整数运算慢得多。在这种情况下,会使用定点运算("Fixed-Point"),小数点数值以整数格式编码,小数部分的长度预先确定。

定点("Fixed-Point")是一种使用普通整数和预定比例(小数部分的位数)表示小数值的方法。它不使用资源密集型浮点运算("Float32"、"Float64"),而是使用一种简单的整数格式,其中 "二进制逗号 "按给定位数移位。

例如,如果小数部分的长度为 ,那么数字 将被存储为整数值 ,因为将内部值(stored_integer)转换为实数值(real_value)的公式 将导致 。从公式中可以看出,内部值被存储并等于 ,但在计算过程中会被解释为

这种运算具有以下优点:

  • 资源消耗少(适用于微控制器、FPGA 和 ASIC);

  • 可控的精度和数值范围;

  • 可预测的四舍五入行为[1]和溢出[2]

  • 支持 Verilog (HDL) 和 C 语言中的代码生成器

在文章中阅读有关定点运算的更多信息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

在哪里?

  • S - 符号(1 - 符号[3], 0 - unsigned[4]);

  • W - 字长(每个数字分配的位数);

  • f - 小数部分的长度(每个小数部分的位数,比例);

  • T - 定点数的整数表示类型(Int32、`UInt64`等)。

由于在数据处理的所有阶段都采用了安全键入和明确的行为方式,因此这种格式可让您准确指定数字的存储、解释和参与计算的方式。


为方便起见,``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)`。

与前一个构造函数类似,但可以明确指定存储类型。类型将根据参数 SWf 自动匹配,与指定的 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   # результат насыщения при переполнении

复数

完全支持定点复数,与通过 fi 创建复数的方法相同:

s, w, f = 1, 62, 7;
v = 2.5 - 3.21im
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)
println()

输出:

fi(2.5, 1, 62, 7) - fi(3.2109375, 1, 62, 7)*im
true
fi(2.5, 1, 62, 59) - fi(3.21, 1, 62, 59)*im

使用数组和矩阵

程序库全面支持定点数的矢量和矩阵运算。所有操作都会保留元素类型,并自动将指定精度参数应用于所有数组元素。

向量

创建并处理一维数组。定点参数适用于所有元素:

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

基本操作和方法

介绍处理定点数的方法,允许您定义允许的数值范围和基本属性。

边界值

通过 typemaxtypemin 方法,可以定义特定定点类型的最大值和最小值。

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);

    • 灵活的小数部分设置(包括负值和 ,小数部分长度大于字长的情况 )。

  • *改进了类型输出规则:

    • 自动检测数学运算结果的格式(&plus;, -, *, /);

    • 移除块Divide/Add/Divide/Gain 中自动类型继承的限制。

  • *根据平台生成代码:

    • 针对目标平台(C 或 Verilog)的不同类型继承规则;

    • 128 位边界溢出时的可预测行为(不同于同类产品)。

  • *功能扩展:

    • 优化了数组和矩阵的处理(参见 C 语言和 Verilog); *可预测 128 位边界溢出时的行为(与同类产品不同 使用数组和矩阵);

    • 完全支持复数(参见 ). 复数);

    • 高效的四舍五入方法(round/trunc/ceil/floor,参见 ). 四舍五入).

    • 支持基本方法(zero/one/typemin/typemax,见 )。 边界值.

实用链接


1. *四舍五入*是将给定小数部分(f)的值转换为有效形式的过程。由于定点数无法准确表示所有可能的分数值,因此运算会根据给定的策略(如取最接近的值或截断)对结果进行舍入。
2. *溢出*是指计算结果超出给定字大小时(W)和符号性时(S)所允许的限制时发生的过程。在这种情况下,将采用溢出处理策略:饱和(将值限制在允许的最大/最小值)、截断或错误输出
3. 符号 - 可以存储正值和负值。例如:Int8, Int16, Fixed{1, 16, 4}.
4. Unsigned - 只能存储正值和零。例如:UInt8, UInt16, Fixed{0, 16, 4}.