Опубликован: 27.09.2006 | Уровень: для всех | Доступ: свободно
Лекция 4:

Особенности представления чисел в ЭВМ

< Лекция 3 || Лекция 4: 123 || Лекция 5 >

Вещественные числа

Обсуждаемые в данной секции вопросы значительно более полно рассмотрены в четвертой главе классической книги Кнута [7].

Для представления вещественных чисел в языке Java используют переменные и константы типов float и double. Величины первого из них занимают четыре байта, а второго — восемь.

В отличие от множества целых чисел \mathbb{Z} вещественные числа \mathbb{R} обладают свойством полноты: между любыми двумя различными числами всегда найдется отличное от них третье. Понятно, что любой из способов представления бесконечного множества вещественных чисел с помощью некоторого конечного множества \mathbb{R}_M не даст возможности сохранить свойство полноты. Наиболее распространенным способом реализации вещественных чисел на ЭВМ является использование чисел с плавающей точкой. При этом множество \mathbb{R}_M оказывается состоящим из элементов вида

f = \pm (\frac{\alpha_1}{p}+\frac{\alpha_2}{p^2}+\ldots+
\frac{\alpha_n}{p^n})p^e,

где целые числа \alpha_i для всех i из диапазона от 1 до n удовлетворяют соотношению 0 \leqslant \alpha_i \leqslant p - 1, а величина e лежит в диапазоне от L до U ( L \leqslant e \leqslant
U ). Число p является при этом основанием системы счисления (чаще всего это 2), константа n задает точность представления чисел (для типа double она больше, чем для float ), а диапазон [L,U] определяет область значений экспоненты (для типа double он также больше, чем для float ).

Число \pm (\frac{\alpha_1}{p}+\frac{\alpha_2}{p^2}+\ldots+
\frac{\alpha_n}{p^n}) принято называть мантиссой числа f с плавающей точкой. Часто требуют, чтобы для всех чисел f \ne 0 величина \alpha_1 была ненулевой. Такие числа с плавающей точкой называют нормализованными.

В языке Java для кодирования величин типов float и double также используют числа с плавающей точкой. При этом часть из имеющегося множества бит используют для размещения экспоненты e, а остальные биты — для размещения мантиссы.

Для того чтобы хорошо понять, что же представляет из себя множество \mathbb{R}_M нормализованных чисел с плавающей точкой, полезно изобразить его на числовой прямой для случая небольших n, L и U. Подобная задача приведена в конце параграфа. Сейчас же нам будет достаточно весьма качественного описания этого множества.

Так как \mathbb{R}_M симметрично относительно начала координат, то можно разобраться только с неотрицательными числами. Нуль, конечно же, принадлежит искомому множеству. Ближайшая к нулю следующая точка получается при \alpha_1 = 1, \alpha_2 = \alpha_3 = \ldots = \alpha_n = 0 и e=L. Это число для чисел типов float и double определено, как MIN_VALUE в классах java.lang.Float и java.lang.Double соответственно.

Правее располагается множество точек, следующих друг за другом с шагом 2^{L-n}. Затем шаг увеличивается. Потом еще. И еще. При e=U расстояние между двумя соседними точками множества \mathbb{R}_M достигает 2^{U-n}. Самая правая точка множества получается при \alpha_1 = \alpha_2 = \ldots = \alpha_n =
1 и e=U. Для типов float и double это число определено, как MAX_VALUE в классах java.lang.Float и java.lang.Double.

Кроме перечисленных (и симметричных им отрицательных) значений в результате выполнения некоторых операций могут получиться также следующие особые значения: плюс бесконечность ( POSITIVE\_INFINITY ), минус бесконечность ( NEGATIVE\_INFINITY ), минус ноль и не число ( NaN ). Например, при делении единицы на минус ноль получается минус бесконечность.

Описанные особенности множества машинных вещественных чисел \mathbb{R}_M приводят к тому, что не для всех его элементов верны следующие соотношения, всегда справедливые для множества настоящих вещественных чисел \mathbb{R}:

  • x+1>x ;
  • x+x существует;
  • (x\cdot 2) / 2 = x ;
  • (x /2) \cdot 2 = x ;
  • a+(b+c) = (a+b) + c ;
  • a+b+c = c+b+a.

Приведем несколько примеров, иллюстрирующих эти особенности множества \mathbb{R}_M.

Задача 4.1. Предъявите действительное (типа double ) число x такое, что x + 1 = x, а (x\cdot 2) / 2 \ne x. Воспользуйтесь тем, что класс java.lang.Double определяет константу MAX_VALUE.

Текст программы

public class DblMaxVal {
    public static void main(String[] args) {    
        double x = Double.MAX_VALUE;
        double y = x + 1.0;

        if (x == y) {
            Xterm.print("Для числа ");
            Xterm.print("x = " + x, Xterm.Blue);
            Xterm.print(" величины x и (x+1) ");
            Xterm.print("равны!\n", Xterm.Red);
        }
	
        y = 2.0 * x;
        double z = y / 2.0;

        if (x != z) {
            Xterm.print("Для числа ");
            Xterm.print("x = " + x, Xterm.Blue);
            Xterm.print(" величины x и (2.0*x)/2.0 ");
            Xterm.print("различны\n", Xterm.Red);
            Xterm.print("и равны соответственно");
            Xterm.print(" " + x, Xterm.Red);
            Xterm.print(" и");
            Xterm.print(" " + z + "\n", Xterm.Red);
        }
    }
}

Задача 4.2.Предъявите такие действительные (типа double ) числа a, b и c такие, что a+(b+c) \ne (a+b)+c.

Достаточно вспомнить, что точки множества \mathbb{R}_M расположены на числовой прямой неравномерно.

Текст программы

public class DblNoAssociative {
    public static void main(String[] args) {    
        double x = 1.0e-16;
	
        double y =  1.  + (x  +  x);
        double z = (1.  +  x) +  x ;

        if (y != z) {
            Xterm.print("Для числа ");
            Xterm.print("x = " + x, Xterm.Blue);
            Xterm.print(" величины 1.+(x+x) и (1.+x)+x ");
            Xterm.print("различны\n", Xterm.Red);
            Xterm.print("и равны соответственно");
            Xterm.print(" " + y, Xterm.Red);
            Xterm.print(" и");
            Xterm.print(" " + z + "\n", Xterm.Red);
        }
    }
}

Иногда даже работа с казалось бы не слишком маленькими или огромными по абсолютной величине числами может привести к удивительным результатам. Рассмотрим задачу о решении квадратного уравнения.

Задача Напишите программу, вводящую действительные коэффициенты a, b и c квадратного уравнения a x^2 + b x + c = 0 с положительным дискриминантом, находящую оба корня этого уравнения.

Вот вполне естественное решение этой задачи, которое может быть написано любым человеком, обладающим минимальными знаниями языка Java.

Текст программы

public class SquareEquation {
    public static void main(String[] args) throws Exception {    
        double a = Xterm.inputDouble("Введите число a -> ");
        double b = Xterm.inputDouble("Введите число b -> ");
        double c = Xterm.inputDouble("Введите число c -> ");

        if (a == 0.) {
            Xterm.println("Уравнение не квадратное", Xterm.Red);
            System.exit(0);
        }

        if (b*b - 4.*a*c <= 0.) {
            Xterm.println("Дискриминант неположителен", Xterm.Red);
            return;
        }

        double x1 = ( -b - Math.sqrt(b*b - 4.*a*c) ) / (2.*a);
        double x2 = ( -b + Math.sqrt(b*b - 4.*a*c) ) / (2.*a);

        Xterm.println("Корни уравнения:");
        Xterm.println("x1 = " + x1, Xterm.Blue);
        Xterm.println("x2 = " + x2, Xterm.Blue);
    }
}

Для извлечения квадратного корня здесь используется метод sqrt класса Math. Вызов метода System.exit и применение оператора return, которые в теле метода main почти эквивалентны и приводят к немедленному завершению выполнения программы, позволяет обойтись без ветви else оператора if-else. Остальная часть программы комментариев не требует.

Попробуйте, однако, выполнить эту программу при a=0.2E-16 и b=c=1.0 — первый корень уравнения x1 окажется равным -5.0E16, а второй x2 — нулю.

Если подставить эти значения в выражение a x^2 + b x + c, то и для x1 и для x2 его значение окажется равным единице, а не нулю. Подобная ошибка для первого, весьма большого по величине корня, представляется вполне естественной, но вот соглашаться с тем, что нуль является корнем данного уравнения, совсем не хочется. Этой проблеме посвящена одна из приведенных ниже задач.

< Лекция 3 || Лекция 4: 123 || Лекция 5 >
Анастасия Халудорова
Анастасия Халудорова
подавляющее большиство фукций на пространстве последовательостей?
екатерина яковлева
екатерина яковлева
как получить сертификат,что для этого нужно?
Дмитрий Карпов
Дмитрий Карпов
Россия, Нижний Новгород
Антон Никитин
Антон Никитин
Россия, Хабаровск