Документация Engee
Notebook

Типы данных с фиксированной точкой

В данном примере рассмотрим возможности взаимодействия с числами с фиксированной точкой как в скриптах, так и в моделях.

Числа с фиксированной точкой – это числа, бинарное представление которых ограничено размерами их целой и дробной части. Данные числа не имеют возможности изменения положения точки в битовом представлении числа.

Рассмотрим реализации, представленные в Engee. Для определения числа с фиксированной точкой используется функция fi(Value, Sign, Total_bits, Fractional_bits). Она позволяет задавать любые числа внутри одной команды, а также в ней отсутствует привязка к фиксированному количеству бит на слово, что, соответственно, позволяет получить алгоритм с оптимальными затратами по памяти.

Теперь рассмотрим, что значат параметры этой функции.

  1. Value – значения исходного числа.
  2. Sign – знак (1-знаковый, 0-беззнаковый типы).
  3. Total_bits – полная разрядность слова.
  4. Fractional_bits – размер дробной части.
In [ ]:
Value = 128.9
Sign = 1;
Total_bits = 16;
Fractional_bits = 7;

println("fi: $(fi(Value, Sign, Total_bits, Fractional_bits))")
fi: 128.8984375

Проведём некоторые тесты для fi() .

Зададим беззнаковое 7 битное число с нулевой целой частью.

In [ ]:
x = fi(0.8,0,7,7)
Out[0]:
fi(0.796875, 0, 7, 7)

Теперь прибавим к нему число с целой частью. Как мы видим, тип числа был автоматически переназначен.

In [ ]:
x + fi(2.8,0,8,6)
Out[0]:
fi(3.59375, 0, 10, 7)

Теперь объявим отрицательное 8-битное число с 5 битами на дробную часть.

In [ ]:
x = fi(-1.8,1,8,5)
Out[0]:
fi(-1.8125, 1, 8, 5)

Умножим его на 3. Как мы можем заметить, итоговое число имеет на 64 бита больше выделяемой памяти на целую часть. Это связано с тем, что тройка имеет тип данных Int64.

In [ ]:
3x
Out[0]:
fi(-5.4375, 1, 72, 5)

Соответственно, если мы выполним операцию вычитания, тип данных также будет переопределён.

In [ ]:
fi(2.8,1,16,11)-x
Out[0]:
fi(4.6123046875, 1, 17, 11)

Помимо этого, мы сами можем явно переназначить тип числа.

In [ ]:
x = fi(x,1,7,5)
Out[0]:
fi(-1.8125, 1, 7, 5)

Как мы видим, представление, реализованное в Engee, – действительно универсальный, а также устойчивый к переполнениям инструмент, так как он автоматически добавляет биты к слову в зависимости от типов данных, применяемых для того или иного числа.

Применение вычислений с фиксированной точкой в моделях

Теперь перейдём к применению чисел с фиксированной точкой в моделях. Для этого в Engee в настройках блоков предусмотрена возможность выбора выходного типа данных. На рисунке ниже показан интерфейс одного из таких блоков.

В данном примере реализована модель ПИД-регулятора с применением логики фиксированной точки. Пропорционально-интегрально-дифференцирующий регулятор — устройство в управляющем контуре с обратной связью. ПИД-регулятор в системе автоматического управления дает управляющий сигнал хорошей точности и высокого качества.

На рисунке ниже показана реализованная нами модель.

Перейдём к запуску этой модели.

In [ ]:
function run_model( name_model, path_to_folder )
    
    Path = path_to_folder * "/" * name_model * ".engee"
    
    if name_model in [m.name for m in engee.get_all_models()] # Проверка условия загрузки модели в ядро
        model = engee.open( name_model ) # Открыть модель
        model_output = engee.run( model, verbose=true ); # Запустить модель
    else
        model = engee.load( Path, force=true ) # Загрузить модель
        model_output = engee.run( model, verbose=true ); # Запустить модель
        engee.close( name_model, force=true ); # Закрыть модель
    end

    return model_output
end
Out[0]:
run_model (generic function with 1 method)
In [ ]:
# Запуск модели
run_model( "simple_model_pid_fixed", @__DIR__ )
Building...
Progress 0%
Progress 78%
Progress 100%
Out[0]:
SimulationResult(
    "SubSystem.command" => WorkspaceArray{Fixed{1, 16, 13, Int16}}("simple_model_pid_fixed/SubSystem.command")

)

Далее для обработки данных моделирования, записанных в CSV, нам понадобится подключить две библиотеки – DataFrames и CSV. Для отображения этих данных задействуем библиотеку Plots.

In [ ]:
# Подключение библиотек
using CSV
using DataFrames
using Plots

Теперь выполним чтение данных из CSV, построим результирующий график и проведём анализ этих данных.

In [ ]:
command_fixed = Matrix(CSV.read("$(@__DIR__)/command_fixed.csv", DataFrame)); #загрузка данных
command_fixed = (command_fixed[:,2]);

Просмотрим структуру записанных данных. Как мы видим ниже, записанные данные представлены в стандартном формате и не требуют дополнительной обработки.

In [ ]:
dump(command_fixed[101])
Float64 3.0198974609375

Построим график записанных данных, а также проведём анализ на основе записанных в процессе моделирования данных с плавающей точкой и сравним их по точности.

In [ ]:
command = Matrix(CSV.read("$(@__DIR__)/command.csv", DataFrame)); #загрузка данных
# Построение графиков 
plot(command[:,2]) 
plot!(command_fixed)
Out[0]:

Как мы видим, графики приблизительно совпадают. Для того, чтобы точно определить погрешность, найдём их разницу.

In [ ]:
sum(command[:,2]-command_fixed)
Out[0]:
0.0

Оказалось, что разница нулевая. Также видно, что в результате применяемая нами логика чисел с фиксированной точкой не повлияла на принцип работы ПИД-регулятора. Несмотря на потерю точности, система стремится к равновесию.

Вывод

В данном примере мы разобрали возможности применения логики с фиксированной точкой в скриптах и в моделях. Также выяснили, как их можно сохранить из модели и провести анализ или продолжить дальнейшую обработку в скриптах.