Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -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
344 changes: 344 additions & 0 deletions Homeworks/07-Real48/real48.tex
Original file line number Diff line number Diff line change
@@ -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}

1 change: 1 addition & 0 deletions Homeworks/07-Real48/submodule
Submodule submodule added at 60ddb9
Loading