diff --git a/.gitmodules b/.gitmodules index 2145bed..4ccaed0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "Homeworks/06-LIFO-Stack/hw-project"] path = Homeworks/06-LIFO-Stack/hw-project url = ../hw-lifo-stack.git +[submodule "Homeworks/07-Real48/submodule"] + path = Homeworks/07-Real48/submodule + url = ../hw-real48.git diff --git a/Homeworks/07-Real48/real48.tex b/Homeworks/07-Real48/real48.tex new file mode 100644 index 0000000..f38c498 --- /dev/null +++ b/Homeworks/07-Real48/real48.tex @@ -0,0 +1,344 @@ +\documentclass[14pt]{extarticle} + +\usepackage{homeworktemplate} +\usepackage[askip=3mm, bskip=3mm]{terminal} +\usepackage[askip=3mm, bskip=3mm]{mylisting} +\usepackage{tcolorbox} +\usepackage{csquotes} +\usepackage{amsmath} +\usepackage{tocloft} + +\renewcommand{\cftsecleader}{\cftdotfill{\cftdotsep}} + +\title{Домашняя работа 6 \\ \enquote{Real48}} + +\begin{document} + + \maketitle + + \tableofcontents + + \section{Описание задания} + + \subsection{Проблема} + + В Delphi существует тип данных \texttt{Real48}\footnotemark{}. + Как можно понять из его наименования, он предназначен для представления чисел + с плавающей точкой в 48-битах (т.е. в 6 байтах). + + \footnotetext{\url{https://docwiki.embarcadero.com/RADStudio/Athens/en/Internal\_Data\_Formats\_(Delphi)\#The\_Real48\_type}} + + Такой способ представления не входит в стандарт IEEE~754\footnote{\url{https://en.wikipedia.org/wiki/IEEE\_754}}, + который де-факто является умолчательным способом реализовывать + арифметику чисел с плавающей точкой (как программно, так и аппаратно) + в современных компьютерах. + В системе типов Delphi он существует только для поддержания обратной + совместимости\footnotemark{}, рекомендуется предпочитать ему + типы \texttt{Double} и \texttt{Single}, которые удовлетворяют + стандарту IEEE~754. + + \footnotetext{\url{https://docwiki.embarcadero.com/Libraries/Athens/en/System.Real48}} + + 6 байт распределены на бит знака, экспоненту и мантиссу следующим образом + (начиная с младшего бита): + + \begin{enumerate} + \item экспонента ($e$) \textemdash \space 8 бит; + \item мантисса ($f$) \textemdash \space 39 бит; + \item знаковый бит ($s$) \textemdash \space 1 бит. + \end{enumerate} + + Если условиться, что $e$, $f$ и $s$ представлены в виде беззнаковых + целых чисел, то кодируемое в \texttt{Real48} число с плавающей точкой + $F$ может быть вычислено по следующей формуле: + + \begin{equation} \label{eq:1} + F = \left(-1\right)^s \cdot 2^{e - 129} \cdot \left(1 + \frac{f}{2^{39}}\right) + \end{equation} + + Если принять мантиссу $f$ как простую последовательность бит, то это же + уравнение можно написать в виде: + + \begin{equation} + F = \left(-1\right)^s \cdot 2^{e - 129} \cdot 1.f + \end{equation} + + Здесь запись $1.f$ \textemdash \space дробное число\footnotemark{} в двоичной системе + счисления. + + \footnotetext{\url{https://en.wikipedia.org/wiki/Binary\_number\#Fractions}} + + У \texttt{Real48} всего одно специальной значение \textemdash \space + ноль. + Оно кодируется $e = 0$. + Значения мантиссы и знакового бита не специфицируются и могут быть + произовольными. + + Не предусмотрены специальные значения для представления NaN, бесконечности + или ненормализированных чисел\footnote{\url{https://www.intel.com/content/www/us/en/docs/dpcpp-cpp-compiler/developer-guide-reference/2023-1/denormal-numbers.html}}. + Последние при сохранении в \texttt{Real48} становятся нулем, + NaN и бесконечность при попытке сохранить их в \texttt{Real48} + приводят к ошибке. + + В домашнем задании вам предлагается написать программную реализацию \texttt{Real48} + в виде C++ класса. + Такой класс может быть полезен при сериализации/десериализации данных + для отправки по сети приложению, написанному на Delphi и ожидающему + данные в виде \texttt{Real48}. + + \subsection{Шаблон проекта} + + В шаблоне проекта (\url{https://github.com/cppdevcourse/hw-real48}) подготовлены + следующие файлы: + + \begin{itemize} + + \item \textit{real48.hpp} \textemdash \space в этом файле написан + интерфейс класса \texttt{Real48}. + Вам необходимо доработать этот интерфейс, добавив приватные + поля и методы, а также дописав определения тех публичных методов, + которые должны быть \texttt{inline} (в строках, где это требуется, + есть комментарий со словом \texttt{TODO}). + + \item \textit{CMakeLists.txt} \textemdash \space + в этом файле описан процесс сборки проекта. + Присутствует опция \texttt{WITH\_TESTS}, которую + нужно включить для сборки проекта с тестами. + По-умолчанию она выключена. + + \item \textit{unit-tests.hpp} \textemdash \space + в этом файле описаны юнит-тесты проекта. + В какой-то степени в нем верифицируются требования из + этого файла. + Также в нем можно найти подсказки по реализации, изучив + код тестов. + + \item \textit{fuzzing-test.hpp} \textemdash \space + в этом файле описан фаззинг-тест класса \texttt{Real48}. + Входные данные фаззинга, в зависимости от их длины, интерпретируются + либо как \texttt{float}, либо как \texttt{double}. + Далее происходит попытка создать объект \texttt{Real48} + из полученного числа с плавающей точкой. + + \end{itemize} + + \subsection{Требования} \label{requirements} + + \subsubsection{Общие требования к классу \texttt{Real48}} + + \begin{enumerate} + + \item Размер объекта \texttt{Real48} должен составлять 6 байт. + + \item Объект должен представлять в памяти число с плавающей точкой + так же, как делает это Delphi. + Т.е., должна быть возможность выполнить \texttt{memcpy} из + объекта в сетевой пакет, отправить это программе на Delphi, и + последняя должна корректно десериализовать число с плавающей точкой. + + \end{enumerate} + + \subsubsection{Конструктор по-умолчанию} + + \begin{myinplacelisting}[minted language=cpp] +constexpr Real48(); + \end{myinplacelisting} + + Конструктор по-умолчанию должен создавать число с плавающей + точкой со специальным значением ноль. + + \subsubsection{Конструкторы из \texttt{float} и \texttt{double}} + + \begin{myinplacelisting}[minted language=cpp] +Real48(const float number); +Real48(const double number); + \end{myinplacelisting} + + \begin{enumerate} + + \item Если параметр является NaN или бесконечностью, то + конструктор должен выбрасывать \texttt{std::overflow\_error}. + + \item Если параметр является нулем или ненормализованным числом, + то конструктор должен создавать число с плавающей + точкой со специальным значением ноль. + + \item Если параметр является нормализованным числом, но не может + быть корректно представлен в \texttt{Real48} (например, слишком + большое $e$, не помещающееся в 8 бит), то конструктор + должен выбрасывать \texttt{std::overflow\_error}. + + \item Если не удалось определить тип параметра (NaN, бесконечность, + ненормализованное число, ноль, нормализованное число), то конструктор + должен выбрасывать \texttt{std::overflow\_error}. + + \item Если параметр $F$ является нормализованным числом и может быть + представлен в \texttt{Real48}, то должно формироваться + число с плавающей точкой по формуле \ref{eq:1}. + + Можно считать, что \texttt{float} и \texttt{double} + закодированы стандартом IEEE~754 в little-endian архитектуре. + Например, для перевода из \texttt{double} можно составить + следующее неравенство: + + \begin{equation} \label{eq:3} + \begin{split} + F = \left(-1\right)^{s_{48}} \cdot 2^{e_{48} - 129} \cdot \left(1 + \frac{f_{48}}{2^{39}}\right) = \\ + \left(-1\right)^{s_{64}} \cdot 2^{e_{64} - 1023} \cdot \left(1 + \frac{f_{64}}{2^{52}}\right) + \end{split} + \end{equation} + + где $s_{48}$, $e_{48}$ и $f_{48}$ \textemdash \space искомые параметры + \texttt{Real48}, а $s_{64}$, $e_{64}$ и $f_{64}$ \textemdash \space + известеные параметры \texttt{double}. + + \end{enumerate} + + \subsubsection{Операторы приведения к \texttt{float} и \texttt{double}} + + \begin{myinplacelisting}[minted language=cpp] +operator float() const; +operator double() const noexcept; + \end{myinplacelisting} + + \begin{enumerate} + + \item Операторы приведения должны выполнять обратное преобразование по + отношению к преобразованиям, производимым конструкторами. + Например, для перевода в \texttt{double} можно воспользоваться + равенством \ref{eq:3}. + + \item Оператор приведения к \texttt{float} должен выбрасывать + \texttt{std::overflow\_error}, если объект \texttt{Real48} + не может быть представлен в \texttt{float}. + + \end{enumerate} + + \subsubsection{Операторы присваивания} + + \begin{myinplacelisting}[minted language=cpp] +Real48& operator+=(const Real48& b); +Real48& operator-=(const Real48& b); +Real48& operator*=(const Real48& b); +Real48& operator/=(const Real48& b); + \end{myinplacelisting} + + Поведение операторов присваивания должно учитывать арифметику + плавающих чисел. + Допустимо использовать арифметику плавающего числа другого типа. + + \subsubsection{Арифметические операторы} + + \begin{myinplacelisting}[minted language=cpp] +Real48 operator+() const noexcept; +Real48 operator-() const noexcept; +Real48 operator+(const Real48& o) const; +Real48 operator-(const Real48& o) const; +Real48 operator*(const Real48& o) const; +Real48 operator/(const Real48& o) const; + \end{myinplacelisting} + + Поведение арифметических операторов должно учитывать арифметику + плавающих чисел. + Допустимо использовать арифметику плавающего числа другого типа. + + \subsubsection{Операторы сравнения} + + \begin{myinplacelisting}[minted language=cpp] +bool operator>(const Real48& o) const noexcept; +bool operator<(const Real48& o) const noexcept; + \end{myinplacelisting} + + Поведение операторов сравнения должно учитывать арифметику + плавающих чисел. + Допустимо использовать арифметику плавающего числа другого типа. + + \subsubsection{Метод \texttt{Classify}} + + \begin{myinplacelisting}[minted language=cpp] +enum class Class +{ + NORMAL, + ZERO +}; +Class Classify() const noexcept; + \end{myinplacelisting} + + Этот метод должен возвращать \texttt{Class::ZERO}, если + число имеет специальное значение ноль, и \texttt{Class::NORMAL} + в ином случае. + + \subsubsection{Метод \texttt{min}} + + \begin{myinplacelisting}[minted language=cpp] +consteval static Real48 min(); + \end{myinplacelisting} + + Метод должен возвращать минимальное положительное значение, которое возможно + представить в \texttt{Real48}, отличное от нуля. + + \subsubsection{Метод \texttt{max}} + + \begin{myinplacelisting}[minted language=cpp] +consteval static Real48 max(); + \end{myinplacelisting} + + Метод должен возвращать максимальное положительное значение, которое возможно + представить в \texttt{Real48}. + + \subsubsection{Метод \texttt{epsilon}} + + \begin{myinplacelisting}[minted language=cpp] +consteval static Real48 epsilon(); + \end{myinplacelisting} + + Метод должен возвращать разницу\footnotemark{} между 1.0 и следующим значением, + которое можно представить в \texttt{Real48}, в нормализованном виде. + Для \texttt{Real48} это число \textemdash \space $2^{-39}$. + + \footnotetext{\url{https://en.wikipedia.org/wiki/Machine\_epsilon}} + + \section{Порядок выполнения} + + \begin{enumerate} + + \item Создайте форк репозитория \url{https://github.com/cppdevcourse/hw-real48}. + + \item Добавьте файл \textit{real48.cpp} и внесите изменения в \textit{real48.hpp}. + + \item Соберите проект и протестируйте решение (если получилось установить зависимости, + необходимые для тестирования). + Этот пункт не обязателен, потому что в pull request сборка и тесты будут выполнены + в рамках GitHub Actions. + + \item Создайте pull request из вашего форка в оригинальный репозиторий, + в названии которого есть ваше ФИО. + + \end{enumerate} + + \section{Критерии выполнения} + + \begin{itemize} + + \item Выполнены все требования из \ref{requirements}. + + \item Pull request проходит автоматизированные проверки. + + \item Код в pull request прошел ревью преподавателем. + + \end{itemize} + + \section{Полезные ссылки} + + При выполнении домашнего задания вам, \textit{возможно}, поможет информация по + следующим ссылкам: + + \begin{itemize} + \item Битовые поля: \url{https://en.cppreference.com/w/cpp/language/bit\_field.html}. + \item Битовые операторы: \url{http://en.cppreference.com/w/cpp/language/operator\_arithmetic.html}. + \item \texttt{\#pragma pack}: \url{https://en.cppreference.com/w/cpp/preprocessor/impl.html}. + \item Манипуляции над последовательностями бит: \url{https://en.cppreference.com/w/cpp/utility/bit.html}. + \end{itemize} + +\end{document} + diff --git a/Homeworks/07-Real48/submodule b/Homeworks/07-Real48/submodule new file mode 160000 index 0000000..60ddb97 --- /dev/null +++ b/Homeworks/07-Real48/submodule @@ -0,0 +1 @@ +Subproject commit 60ddb97fb8a6d4d10417deafe5b10913fcb3a8ce diff --git a/Makefile b/Makefile index d7fbb78..b42ecb0 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,7 @@ BUILD_DIR := build hw-04 \ hw-05 \ hw-06 \ + hw-07 \ install \ pr-01 \ pr-02 \ @@ -38,6 +39,7 @@ build: \ hw-04 \ hw-05 \ hw-06 \ + hw-07 \ pr-01 \ pr-02 \ pr-03 \ @@ -76,6 +78,7 @@ install: build cp "$(BUILD_DIR)/hw-04.pdf" "$(PREFIX)/Домашние задания/04 The Guessing Game.pdf" cp "$(BUILD_DIR)/hw-05.pdf" "$(PREFIX)/Домашние задания/05 Hide Secret.pdf" cp "$(BUILD_DIR)/hw-06.pdf" "$(PREFIX)/Домашние задания/06 LIFO Stack.pdf" + cp "$(BUILD_DIR)/hw-07.pdf" "$(PREFIX)/Домашние задания/07 Real48.pdf" mkdir -p "$(PREFIX)/Проекты" cp "$(BUILD_DIR)/prj-auth-lib.pdf" "$(PREFIX)/Проекты/Библиотека идентификации и аутентификации.pdf" cp "$(BUILD_DIR)/prj-enc-exch.pdf" "$(PREFIX)/Проекты/Baremetal шифрование обмена.pdf" @@ -94,6 +97,7 @@ help: @printf "hw-04\tbuild homework hw-04.pdf\n" @printf "hw-05\tbuild homework hw-05.pdf\n" @printf "hw-06\tbuild homework hw-06.pdf\n" + @printf "hw-07\tbuild homework hw-07.pdf\n" @printf "install\tinstall all presentations and homeworks\n" @printf "pr-01\tbuild presentation pr-01.pdf\n" @printf "pr-02\tbuild presentation pr-02.pdf\n" @@ -131,6 +135,7 @@ hw-03: hw-03.pdf hw-04: hw-04.pdf hw-05: hw-05.pdf hw-06: hw-06.pdf +hw-07: hw-07.pdf prj-auth-lib: prj-auth-lib.pdf prj-enc-exch: prj-enc-exch.pdf @@ -329,6 +334,13 @@ hw-06.pdf: \ Packages/mylisting.sty $(call generate_pdf,$<,$@) +hw-07.pdf: \ + Homeworks/07-Real48/real48.tex \ + Homeworks/homeworktemplate.sty \ + Packages/terminal.sty \ + Packages/mylisting.sty + $(call generate_pdf,$<,$@) + prj-auth-lib.pdf: \ Projects/Identification-and-Authentication-Library/auth-library.tex \ $(wildcard Projects/images/*.jpg) \