Строго говоря, аналогичная проблема может возникнуть и со знаковыми типами, если границы цикла for переходят допустимый диапазон этих чисел, просто циклы, переменная которых принимает значения вблизи границ диапазона типа Integer
, встречаются гораздо реже.
3.2. Неочевидные особенности вещественных чисел
Если рассмотренные в предыдущих разделах особенности целых чисел могли быть неочевидными только начинающим, то вещественные числа могут преподнести сюрпризы даже достаточно опытным программистам, т. к. их поведение существенно дальше от интуитивных представлений, и эти неожиданности не ограничиваются выходом та пределы диапазона. Существующая литература по Delphi, в основном, считает этот вопрос несущественным и обходит его стороной, в результате чего программист, впервые столкнувшийся с одним из таких сюрпризов, впадает в недоумение и испытывает желание "попрыгать вокруг компьютера с бубном". Здесь мы попытаемся восполнить этот пробел и показать, что необъяснимые на первый взгляд явления на самом деле просты и предсказуемы, если известно, как реализуется вещественная арифметика компьютером.
3.2.1. Двоичные дроби
Для начала — немного математики. В школе мы проходим два вида дробей простые и десятичные. Десятичные дроби, по сути дела, представляют собой разложение числа по степеням десяти. Так, запись 13,6704 означает число, равное 1·101 + 3·100 + 6·10-1 + 7·10-2 + 0·10-3 + 4·10-4. Но внутреннее представление всех чисел в компьютере, в том числе и вещественных, не десятичное, а двоичное. Поэтому он использует двоичные дроби. Они во многом похожи на десятичные, но основанием степени у них служит двойка. Так, двоичная дробь 101.1101 — это 1·22 + 0·21 + 1·20 + 1·2-1 + 1·2-2 + 0·2-3 + 1·2-4. В десятичном представлении это число равно 5,8125, в чем нетрудно убедиться с помощью любого калькулятора.
Теперь вспомним научный формат записи десятичного числа. Первым в этой записи идет знак числа (плюс или минус). Дальше идет так называемая
3.2.2. Вещественные типы Delphi
В Delphi существует четыре вещественных типа: Single
, Double
, Extended
и Real
. Их общий формат одинаков (рис. 3.1, а).
Знак — это всегда один бит. Он равен нулю для положительных чисел и единице для отрицательных. Что же касается размеров мантиссы и экспоненты, то именно в них и заключается различие между типами.
Прежде чем перейти к конкретным цифрам, рассмотрим подробнее тип Real
, сделав для этого небольшой экскурс в историю. Real
— это стандартный тип языка Паскаль, присутствовавший там изначально. Когда создавался Паскаль, процессоры еще не имели встроенной поддержки вещественных чисел, поэтому все операции с данными типа Real сводились к операциям с целыми числами. Соответственно, размер полей в типе Real
был подобран так, чтобы оптимизировать эти операции.
а) общий вид вещественного числа
б) Двоичное представление числа типа Single
Рис. 3.1. Хранение вещественного числа в памяти
Микропроцессор Intel 8086/88 и его улучшенные варианты — 80286 и 80386 — также не имели аппаратной поддержки вещественных чисел. Но у систем на базе этих процессоров была возможность подключения так называемого сопроцессора. Эта микросхема работала с памятью через шины основного процессора и обеспечивала аппаратную поддержку вещественных чисел. В системах средней руки гнездо сопроцессора обычно было пустым, т. к. это уменьшало цену (разумеется, вставить туда сопроцессор не было проблемой). Для каждого центрального процессора выпускались свои сопроцессоры, маркировавшиеся Intel 8087, 80287 и 80387 соответственно. Были даже сопроцессоры, выпускаемые другими фирмами. Они работали быстрее, чем сопроцессоры Intel, но появлялись на рынке позже. Тип вещественных чисел, поддерживаемый сопроцессорами, не совпадает с Real
. Он определяется стандартом IEEE (Institute of Electrical and Electronics Engineers).