Вещественные числа в языке Ада. Введение

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

Вещественные числа (иногда их называют действительные числа) - это числа вида 0.1, 0.04, -10.9 и т.д. (т.е. они содержат дробную часть).

Вещественные числа в Аде можно отнести к плавающим типам либо к фиксированным типам.

Плавающие типы:

Вещественные типы с плавающей точкой имеют неограниченный диапазон значений и точность, определяемую количеством десятичных цифр после запятой (далее по тексту эта точность будет обозначаться буквой D).

В общем виде синтаксис плавающих чисел выглядит так (в квадратных скобках приведены необязательные значения):

[знак] целое.целое [E порядок] -- 3.5, 3.5E-1

Запись 3.5E-1 означает 3.5 * 10 ** (-1), т.е. 3.5 умноженное на 10 в степени -1, т.е. 0.35

В наших программах мы будем пользоваться записями попроще (0.35, 2.15 и т.д.). Но указанная запись считается основной. Обращаю Ваше внимание, что для разделения целой и дробной частей используется точка, а не запятая.

В языке Ада существуют предопределённые плавающие типы, например, Float (GNAT также предоставляет типы Long_Float и Long_Long_Float).

Для ввода-вывода переменных типа Float используется пакет Ada.Float_Text_IO:

with Ada.Float_Text_IO; use Ada.Float_Text_IO;
with Ada.Text_IO; use Ada.Text_IO;             --для New_Line
 
procedure main is
begin
	Put(Float'First);
	New_Line;
	Put(Float'Last);
end main;

Общее описание плавающего типа (в квадратных скобках приведены необязательные элементы):

type Т is digits D [range L .. R];

  • T - Это имя создаваемого типа.
  • Число D — минимальное требуемое число десятичных цифр (значащих) после точки. Это число должно быть обязательно целым числом и быть больше 0. Следует учесть один момент: если, например, D будет равно 8, то для обеспечения такой точности будет достаточно 38 разрядов, однако для архитектуры x32 такая точность невыполнима и программа станет непереносимой между платформами.
  • Значения L и R называются соответственно нижней границей и верхней границей диапазона.

Предопределенный вещественный тип с плавающей точкой Float обеспечивает точность в шесть десятичных цифр после запятой:

type Float is digits 6 range -16#0.FFFF_FF#E+32 .. 16#0.FFFF_FF#E+32; -- -3.40282E+38..3.40282E+38

Здесь определяется тип Float с точностью (количеством десятичных цифр после запятой) D = 6.

Запись 16#0.FFFF_FF#E+32 - запись числа 3.40282E+38 в шестнадцатеричной форме. Расшифруем её:

1. Цифра 16 в данном случае означает систему исчисления (СИ) (шестнадцатеричная, может быть двоичная, восьмеричная, десятичная и т.д.).

2. Символы "#" обрамляют само число.

3. Символ "_" - разделитель между разрядами числа. Он допускается не только в шестнадцатеричной СИ, но и в любой другой. Например, записи 100000 и 100_000 аналогичны. Использовать или нет разделитель "_" - дело вкуса, но запись 100_000 воспринимается глазами лучше.

4. Символы E+32 означает "умножить на десять в тридцать второй степени"

Как и для целых типов в Аде существует возможность определять типы и подтипы для плавающих типов.

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

Объявление плавающих типов выглядит примерно так:

type <Плавающий Тип> is digits 6;
type <Плавающий Тип> is digits 6 range -100.54..100.54;
type <Плавающий Тип> is New <Предопределенный Плавающий Тип>;
subtype T is <Плавающий Тип> digits D [range <Плавающий Тип>(L) .. <Плавающий Тип>(R)];

Примеры определения типов и подтипов:

type COEFFICIENT is digits 10 range -1.0 .. 1.0;
type REAL is digits 8;
type MASS is digits 7 range 0.0 .. 1.0E35;
subtype SHORTCOEFF is COEFFICIENT digits 5;   -- подтип с  меньшей точностью
subtype PROBABILITY is REAL range 0.0 .. 1.0; -- подтип с меньшим диапазоном

Рассмотрим пример задачи: дано положительное действительное число X. Выведите его дробную часть.

Формат входных данных:
Вводится положительное действительное число.
Формат выходных данных:
Выведите ответ на задачу.

Пример ввода:
17.9
Пример вывода:
0.9

with Ada.Float_Text_IO; use Ada.Float_Text_IO;  --Подключаем пакеты для работы с типом Float
 
procedure main is
	n : Float;
begin
	Get(n);
	n := n - Float'Floor(n); -- Float'Floor() - округление "Вниз" (у положительного
                                 --числа это означает "отбросить дробную часть")
	Put(Item => n, Fore => 1, Aft => 2, Exp => 0); --Вывод вещественного числа,
                                                       --можно так: Put(x, 1, 2, 0);
end main;

Разберём эту задачу. В пакете Ada.Float_Text_IO описан тип Float и команды для работы с этим типом. В первой строке программы мы подключаем этот пакет.

Как описано в комментарии, Float'Floor(n) - это округление числа "вниз", т.е. у положительного числа будет отброшена целая часть, а отрицательное число будет уменьшено до ближайшего целого (например, Float'Floor(10.6) = 10.0, а Float'Floor(-10.6) = -11).

Вывод Put(Item => n, Fore => 1, Aft => 2, Exp => 0) - это вывод вещественного числа. Вместо этой формы можно использовать более короткую запись: Put(x, 1, 2, 0).

Здесь:

  • Item - это само выводимое число.
  • Fore - количество цифр до точки (если указать их количество меньше, чем содержится в самом числе, то поле Fore будет автоматически расширено до нужной ширины)
  • Aft - количество цифр, выводимых после точки.
  • Exp - экспоненциальное представление числа (если 0, то вывод будет в обычной форме)

 

Фиксированные типы:

Представление чисел с фиксированной точкой имеет более ограниченный диапазон значений и указанную абсолютную погрешность, которая задается как delta этого типа. Значение должно быть вещественным числом больше 0.

Зачем нужен фиксированный тип? Во-первых, расчёты с фиксированным типом выполняются намного быстрее, чем с плавающим. Во-вторых, зачастую высокая точность расчётов не нужна. Например, в магазинах копейки считаются с точностью до двух знаков после точки и использовать более двух знаков смысла нет.

Общее описание фиксированного типа: type T is delta D range L .. R

Можно расписать определение типов и подтипов следующим образом:

type <Фиксированный тип> is new <Предопределенный Фиксированный Тип>;
subtype T is <Фиксированный тип> delta D range <Фиксированный тип>(L) .. <Фиксированный тип>(R);

Например, в Аде предоставлен предопределенный вещественный тип с фиксированной точкой Duration, который используется для представления времени и обеспечивает точность измерения времени в 50 микросекунд:

type Duration is delta 0.000000001 range -((2 ** 63 - 1) * 0.000000001) .. +((2 ** 63 - 1) * 0.000000001);

Примеры:

type VOLT is delta 0.125 range 0.0 .. 255.0;   --погрешность - 0.125
subtype ROUGH_VOLTAGE is VOLT delta 1.0;       --диапазон, как у VOLT, погрешность - 1.0

Вообще, типы - один из мощнейших инструментов языка Ада. Тема типов и их производных весьма обширна. Я буду вводить элементы по мере возникновения необходимости в них при решении задач. Для тех же, кто хочет досконально разобраться в системе типов языка Ада, советую обратится к книге Александра Гавва "Адское программирование".

Ещё хотелось бы отметить, что числа вещественного типа сравнивать друг с другом нужно очень осторожно, так как представление вещественных чисел в компьютере не точное. Например, число 0.1 в ПК может храниться как 0.09999999. Для сравнения вещественные числа можно, например, преобразовать в целые:

a := 0.1
b := 0.125
if Integer(a * 1000.0) < Integer(b * 1000.0) then...

Тогда в сравнение можно заложить свою допустимую для данного примера погрешность.

Вообще сравнение вещественного типа - это проблема не Ады, а любого языка программирования, так как связана она не с реализацией компилятора, а с представлением чисел в компьютере.

Если изучение материала этой страницы не вызвало у Вас никаких затруднений, то Вы можете смело переходить к решению задач по этой теме.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *