Каковы преимущества разделения целых и действительных дробных чисел в компьютере
Обновлено: 21.11.2024
"Константа с плавающей запятой" – это десятичное число, представляющее вещественное число со знаком. Представление вещественного числа со знаком включает целую часть, дробную часть и показатель степени. Используйте константы с плавающей запятой для представления значений с плавающей запятой, которые нельзя изменить.
Синтаксис
константа с плавающей запятой :
дробная константа часть экспоненты opt плавающий-суффикс opt
цифровая-последовательность степенная-часть плавающий-суффикс выбрать
дробная константа :
последовательность цифр opt . последовательность цифр
последовательность цифр .
часть-показатель :
e знак opt последовательность-цифр
E знак opt последовательность цифр
знак : один из
+ -
плавающий-суффикс : один из
f l F L
Можно опустить либо цифры до запятой (целая часть значения), либо цифры после запятой (дробная часть), но не то и другое одновременно. Вы можете опустить десятичную точку, только если вы включаете показатель степени. Никакие пробельные символы не могут разделять цифры или символы константы.
Следующие примеры иллюстрируют некоторые формы констант и выражений с плавающей запятой:
Константы с плавающей запятой являются положительными, если им не предшествует знак минус (-). В этом случае знак минус рассматривается как унарный арифметический оператор отрицания. Константы с плавающей запятой имеют тип float , double или long double .
Константа с плавающей запятой без суффикса f , F , l или L имеет тип double . Если буква f или F является суффиксом, константа имеет тип float. Если к нему добавляется буква l или L , он имеет тип long double . Например:
Компилятор Microsoft C внутренне представляет long double так же, как type double . Тем не менее, типы различны. Информацию о типах double, float и long double см. в разделе Хранение базовых типов.
Вы можете опустить целую часть константы с плавающей запятой, как показано в следующих примерах. Число 0,75 можно выразить разными способами, включая следующие примеры:
Мой вопрос: почему бы не использовать дроби? Дробь — это просто два числа, которые при делении друг на друга дают интересующее вас число. Итак, представьте себе структуру, которая содержит одно целое число любого размера и по одному байту для каждого из делителя и делимого. И, наверное, все мы помним, как складывать или вычитать дроби со школы.
Я действительно не рассчитывал это, но кажется интуитивно понятным, что преимущества по сравнению с плавающей запятой будут следующими:
- более быстрое сложение/вычитание
- проще печатать
- меньше кода
- легко объединить эти значения в нижний индекс массива (для синусоидальных таблиц и т. д.)
- проще для неспециалистов
- х - х == 0
И недостатки:
- вероятно, диапазон не такой большой, но его достаточно легко преодолеть
- не так точно с очень маленькими числами
- рентабельность?
Это кажется таким очевидным компромиссом (точность/скорость), что я удивлен, что не могу найти домашние компьютеры или BASIC, которые выполняли бы арифметические операции таким образом. Что я упускаю?
Не уверен, что точно понимаю, что вы имеете в виду, но я вижу таблицы поиска, а таблицы поиска занимают память. Память — это то, что вы не хотите тратить впустую на машине, которая имеет только 64 КБ непрерывной памяти.
На самом деле почти все представления чисел с плавающей запятой на домашних (и всех других) компьютерах делают используют дробные представления чисел, но по очевидным причинам ограничивают знаменатели степенью двойки.< /p>
Арифметика с фиксированной точкой вычисляется дробями, только с глобальным неявным знаменателем. Но вопрос явно не об этом.
Рациональное сложение и вычитание медленнее, чем числа с плавающей запятой, а не быстрее. К тому же трудно получить правильный результат с ограниченной точностью (очевидный метод имеет тенденцию к переполнению).
@phuclv Не отвечайте на вопросы в разделе комментариев. Если вы считаете, что другие ответы неверны, напишите новый ответ.
11 ответов 11
При сложении или вычитании дробей необходимо найти наименьшее общее кратное двух знаменателей. Это дорогостоящая операция, гораздо более дорогостоящая, чем сложение или вычитание чисел с плавающей запятой, для которых просто требуются сдвиги.
Умножение также обходится дороже, потому что теперь вам нужно умножать два числа вместо одного. Аналогично для деления.
Итак: это дороже, нет возможности ограничить память, используемую для действительно общих приложений, а научные приложения в любом случае ориентированы на использование с плавающей запятой.
И в тот момент, когда вы сталкиваетесь со знаменателями простых чисел, они могут очень быстро стать очень большими. У него нет предсказуемого верхнего предела. Непредсказуемость использования памяти и скорости вычислений делает дробное использование очень непривлекательным. В крайних случаях, если ошибки округления слишком велики, вы можете переключиться на дроби для повышения точности, но это очень специфическая оптимизация. Переводить все в дроби ненадежно.
Возможно, стоит упомянуть, что не все значения можно представить в виде рациональных чисел, и их все равно необходимо аппроксимировать. Вероятно, с дробью со «сложным» знаменателем.
«Научные приложения ориентированы на использование чисел с плавающей запятой». На самом деле все как раз наоборот. Форматы с плавающей запятой разработаны специально для нужд научных вычислений.
@dirkt: Существуют некоторые основанные на таблицах подходы к умножению на 6502, которые используют тот факт, что A*B == ((A+B)*(A+B)-(AB)*(AB))/ 2 . Учитывая таблицу (N*N)/2 , можно сократить вычисление до Table[A+B]-Table[A-B] . Если одно множимое будет использоваться гораздо чаще, чем другое, и правильно установлены указатели таблицы, sec / lda (ptr1), y / sbc (ptr3), y / sta resultL / lda (ptr3), y / sbc (ptr4), y / sta resultH умножит значение B, используемое для подготовки указателей, на значение в y , что даст 16-битный результат. Я не знаю, как это красиво сделать для деления.
Мой вопрос: почему бы не использовать дроби?
Быстрый ответ:
- Требуется слишком много кода
- Требуется динамическое хранилище
- Длинное представление даже для простых чисел
- Сложное и медленное выполнение
И самое заметное:
- Поскольку числа с плавающей запятой уже основаны на дробях:
Двоичные, с которыми двоичный компьютер справляется лучше всего.
Полная форма:
Мантисса числа с плавающей запятой – это последовательность дробей по основанию двойки:
Каждый из них содержит ноль или единицу, обозначающую наличие этой дроби. Таким образом, отображение 3/8 дает последовательность 0110000.
Дробь – это всего лишь два числа, которые при делении друг на друга дают нужное вам число. Итак, представьте себе структуру, содержащую одно целое число любого размера и один байт для каждого из делителя и делимого.
То, что "любой размер" в конечном итоге является самым важным аргументом против. Простая в реализации система требует как можно более короткого представления, чтобы сэкономить на использовании памяти — это преимущество, особенно на ранних машинах, — и она должна использовать блоки фиксированного размера, чтобы управление памятью могло быть максимально простым.
Каждый байт может не подходить для представления необходимых дробей, что приводит к довольно сложной головоломке нормализации, для решения которой требуется довольно большое количество делений. Действительно дорогостоящая операция (жесткая или программная). Помимо проблем с хранением чисел, это потребует еще большего адресного пространства для хранения нетривиальных процедур обработки.
Двоичный (*1) формат с плавающей запятой основан на вашем представлении о дробях, но доводит его до логического конца. С бинарным FP нет необходимости во многих сложных операциях.
- Преобразование десятичного числа FP в двоичное — это всего лишь серия операций сдвига.
- Возврат к десятичному виду (*2) снова означает просто сдвиг плюс сложение.
- Для сложения или вычитания двух чисел требуется только сложение двоичных целых чисел после сдвига меньшего из них вправо.
- Умножение или деление означает умножение или деление этих двух целых чисел с фиксированной точкой и добавление показателя степени.
Все сложные задачи сводятся к целочисленным операциям фиксированной длины. Не только самая простая форма, но и именно то, что бинарные процессоры могут делать лучше всего. И хотя эта длина может быть адаптирована к работе (*3), уже довольно экономные (по размеру) с потребностью всего в 4 байта памяти покроют большинство повседневных потребностей. А увеличение этого числа до 5,6 или 8 дает точность, которая редко требуется (*4).
И, наверное, все мы помним, как складывать и вычитать дроби еще со школы.
Нет, не совсем так. Для меня это было чем-то, что упоминалось только в течение короткого времени в третьем классе. Имейте в виду, что большая часть мира уже перешла на (десятичные) числа с плавающей запятой более века назад.
*1 - Или аналогичные системы, такие как IBM с плавающей запятой base-16, используемая в серии /360. Здесь базовая единица хранения — это не бит, а фрагмент, подтверждающий, что память ориентирована на байты, а части машины ориентированы на полубайты.
*2 – наименее часто выполняемая операция.
*3 - Уже 16-битная плавающая точка может быть полезна для повседневных задач. Я даже помню приложение с 8-битным форматом с плавающей запятой, которое использовалось для масштабирования приоритетов.
*4 - Да, бывают случаи использования, когда для получения точных/необходимых результатов требуется либо большая точность, либо другая система, но их количество невелико и особенное - или уже охвачено целыми числами:)
В реальной жизни мы имеем дело с действительными числами — числами с дробной частью. Большинство современных компьютеров имеют встроенную (аппаратную) поддержку чисел с плавающей запятой. Однако использование плавающей запятой не обязательно является единственным способом представления дробных чисел. В этой статье описывается представление вещественных чисел с фиксированной точкой. Использование типа данных с фиксированной точкой широко используется в цифровой обработке сигналов (DSP) и игровых приложениях, где производительность иногда важнее точности. Как мы увидим позже, арифметика с фиксированной запятой намного быстрее, чем арифметика с плавающей запятой.
Напомним, что двоичное число:
представляет значение:
1 * 2 5 + 1 * 2 4 + 0 * 2 3 + 1 * 2 2 + 0* 2 1 + 1 * 2 0
= 32 + 16 + 4 + 1
= 5310
Теперь, если мы разделим число 53 на 2, мы знаем, что результат должен быть 26,5. Однако как мы можем его представить, если у нас есть только целочисленные представления?
Ключом для представления дробных чисел, таких как 26,5 выше, является концепция двоичной точки. Двоичная точка похожа на десятичную точку в десятичной системе. Он действует как разделитель между целой и дробной частью числа.
В десятичной системе десятичная точка обозначает положение в числительном, на которое коэффициент должен умножаться на 10 0 = 1. Например, в числительном 26,5 коэффициент 6 имеет вес 10 0 = 1. Но что случилось с 5 справа от запятой? Мы знаем из нашего опыта, что он несет вес 10 -1 . Мы знаем, что число "26,5" представляет собой значение "двадцать шесть с половиной", потому что
Та же самая концепция десятичной точки может быть применена к нашему двоичному представлению, образуя "двоичную точку". Как и в десятичной системе, двоичная точка представляет собой коэффициент члена 2 0 = 1. Все цифры (или биты) слева от двоичной точки имеют вес 2 0 , 2 1 , 2 2 и так далее. Цифры (или биты) справа от двоичной точки имеют вес 2-1, 2-2, 2-3 и так далее. Например, число:
представляет значение:
2 5 2 4 2 3 < td>2 22 1 2 0 2 -1 2 -2 2 -3 . 1 1 0 1< /td> 0 1 0 .
= 1 * 2 4 + 1 * 2 3 + 0 * 2 2 + 1 * 2 1 + 0 * 2 0 + 1 * 2 -1
Внимательный читатель должен теперь понять, что битовая комбинация 53 и 26,5 абсолютно одинакова. Единственная разница — это положение двоичной точки. В случае 5310 двоичной точки нет. В качестве альтернативы мы можем сказать, что двоичная точка расположена справа, в позиции 0. (В десятичном виде 53 и 53,0 представляют одно и то же число.)
2 5 2 4 2 3 < td>2 22 1 2 0 Двоичная точка 2 -1 2 - 2 2 -3 1 1 0 1 0 1 . 0 0 0 td>
В случае 26,510 двоичная точка находится на одну позицию левее от 5310:
2 5 2 4 2 3 < td>2 22 1 2 0 Двоичная точка 2 -1 2 - 2 2 -3 0 1 1 0 1 0 . 1 0 0 td>
Теперь, напомним, на уроке мы обсуждали, что сдвиг целого числа вправо на 1 позицию бита эквивалентен делению числа на 2. В случае целого числа, поскольку у нас нет дробной части, мы просто не можем представить цифра справа от двоичной точки, превращая этот процесс сдвига в целочисленное деление. Однако это просто ограничение целочисленных представлений двоичного числа.
В общем, математически, при фиксированной позиции двоичной точки сдвиг битового шаблона числа вправо на 1 бит всегда делит число на 2. Аналогично, сдвиг числа влево на 1 бит умножает число на 2.
Описанный выше процесс сдвига является ключом к пониманию представления чисел с фиксированной точкой. Чтобы представить вещественное число в компьютерах (или любом оборудовании в целом), мы можем определить тип числа с фиксированной точкой, просто неявно зафиксировав двоичную точку так, чтобы она находилась в некоторой позиции числа. Затем мы просто будем придерживаться этого неявного соглашения при представлении чисел.
Для концептуального определения типа с фиксированной точкой нам нужны всего два параметра:
- ширина представления числа и
- позиция двоичной точки в числе
Дальше в этой статье мы будем использовать обозначение fixed, где w обозначает количество битов, используемых в целом (ширина числа), а b обозначает позицию двоичной точки, считая от младшего значащего бита (считая с 0).
представляет собой действительное число:
= 1 * 2 1 + 1 * 2 -1 + 1 * 2 -1
Обратите внимание, что на компьютере битовая последовательность может означать что угодно. Следовательно, тот же битовый шаблон, если мы "преобразуем" его в другой тип, такой как тип fixed, будет представлять число:
= 1 * 2 -1 + 1 * 2 -3 + 1 * 2 -4
= 0,5 + 0,125 + 0,0625
Если мы рассматриваем этот битовый шаблон как целое число, он представляет собой число:
= 1 * 2 4 + 1 * 2 2 + 1 * 2 1
До сих пор мы говорили о положительных числах, но мы ведь хотим представлять отрицательные числа, не так ли? Как тогда мы представляем отрицательные числа с фиксированной точкой?
В компьютере мы используем дополнение до 2 для представления отрицательных чисел. Одним из свойств дополнительных чисел до 2 является то, что арифметические операции как с положительными, так и с отрицательными числами идентичны. Он включает в себя сложение, вычитание и, что неудивительно, сдвиг. Мы можем разделить отрицательные дополнительные числа 2 на 2 с помощью простого 1-битного сдвига вправо с расширением знака, как мы можем сделать это с положительными числами.
Вспомните, в начале этой статьи мы обсуждали, как числа с фиксированной точкой представляют собой просто сдвинутую версию целого числа (путем установки двоичной точки в ненулевое положение). В сочетании с наблюдением, что операция сдвига применяется как к отрицательным числам в дополнении до 2, так и к положительным числам, мы можем легко увидеть, как мы можем представить отрицательное число в фиксированной точке: используйте дополнение до 2.
В качестве иллюстрации ниже приведены все числа, которые можно представить с помощью 4-битного дополнения до 2:
Битовый шаблон Представленное число (n) n / 2 1 1 1 1 -1 -0,5 1 1 1 0 -2 -1 1 1 0< /td> 1 -3 -1,5 1 1 td> 0 0 -4 -2 1 0 1 1 -5 -2,5 1 0 1 0 -6 -3 1 0 0 1 -7 -3,5 1 0 0 0 - 8 -4 0 1 1 1< /td> 7 3.5 0 1 1 0 6 3 0 1 0 1 5 2,5 0 1 0 0 4 2 0 0 1 1 3 td> 1,5 0 0 1 0 < td>21 0 0 0 1 1 0,5 0 0 0 0 0 0
Глядя на эту таблицу, мы можем легко понять, что можем представить число -2,5 битовым шаблоном "1011", ЕСЛИ мы предполагаем, что двоичная точка находится в позиции 1.
К настоящему времени вы должны обнаружить, что числа с фиксированной точкой действительно близки к целочисленному представлению. Эти два отличаются только положением двоичной точки. На самом деле, вы можете даже рассматривать целочисленное представление как «особый случай» чисел с фиксированной запятой, где двоичная точка находится в позиции 0. Таким образом, все арифметические операции, которые компьютер может выполнять с целыми числами, можно применять и к числам с фиксированной запятой.
Поэтому преимущество арифметики с фиксированной точкой заключается в том, что она столь же проста и эффективна, как арифметика целых чисел в компьютерах. Мы можем повторно использовать все аппаратное обеспечение, созданное для целочисленной арифметики, для выполнения арифметики с вещественными числами с использованием представления с фиксированной точкой. Другими словами, арифметика с фиксированной точкой доступна на компьютерах бесплатно.
Недостаток числа с фиксированной запятой, конечно, заключается в потере диапазона и точности по сравнению с представлениями чисел с плавающей запятой. Например, в фиксированном представлении наша дробная часть имеет точность только до кванта 0,5. Мы не можем представить число как 0,75. Мы можем представить 0,75 с помощью fixed , но тогда мы теряем диапазон целой части.
C не имеет собственного "типа" для числа с фиксированной точкой. Однако из-за природы представления с фиксированной точкой оно нам просто не нужно. Напомним, что все арифметические операции с числами с фиксированной точкой такие же, как и с целыми, мы можем просто повторно использовать целочисленный тип int в C для выполнения арифметических операций с фиксированной точкой.Положение двоичной точки имеет значение только в тех случаях, когда мы печатаем ее на экране или выполняем арифметические действия с другим «типом» (например, при добавлении int к fixed).
Фиксированная точка — это простой, но очень эффективный способ представления дробных чисел в компьютере. За счет повторного использования всех целочисленных арифметических схем компьютера арифметика с фиксированной запятой на несколько порядков быстрее, чем арифметика с плавающей запятой. По этой причине он используется во многих играх и приложениях DSP. С другой стороны, ему не хватает диапазона и точности, которые предлагает представление чисел с плавающей запятой. Вы, как программист или проектировщик схем, должны идти на компромисс.
9.1 Плавающая точка
Одной отличительной чертой, которая отличает традиционную информатику от научных вычислений, является использование дискретной математики (0 и 1) вместо непрерывной математики и вычислений. Переход от целых чисел к действительным числам — это больше, чем косметическое изменение. Цифровые компьютеры не могут точно представлять все действительные числа, поэтому мы сталкиваемся с новыми проблемами при разработке компьютерных алгоритмов для действительных чисел. Теперь, в дополнение к анализу времени выполнения и объема памяти, мы должны позаботиться о «правильности» полученных решений. Эта сложная проблема еще больше усугубляется, поскольку многие важные научные алгоритмы делают дополнительные приближения для работы на дискретном компьютере. Точно так же, как мы обнаружили, что некоторые дискретные алгоритмы по своей природе слишком медленные (полиномиальные или экспоненциальные), мы увидим, что некоторые алгоритмы с плавающей запятой слишком неточны (стабильные или нестабильные). Иногда эту проблему можно исправить, разработав более умный алгоритм. В случае дискретных задач трудность иногда носит внутренний характер (NP-полнота). В задачах с плавающей запятой трудность также может быть присущей (плохая обусловленность), например, точное долгосрочное прогнозирование погоды. Чтобы быть эффективным специалистом в области вычислений, мы должны уметь соответствующим образом классифицировать наши алгоритмы и задачи.
С плавающей точкой.
Некоторые из величайших достижений 20-го века были бы невозможны без возможностей цифровых компьютеров с плавающей запятой. Тем не менее, эта тема не совсем понятна большинству программистов и является постоянным источником путаницы. В феврале 1998 г. в программной речи под названием Расширения Java для численных вычислений Джеймс Гослинг утверждал, что «95% людей совершенно ничего не смыслят в операциях с плавающей запятой». Однако основные идеи, лежащие в основе работы с плавающей запятой, несложны, и мы демистифицируем путаницу, с которой сталкивается большинство новичков.
Двоичное представление с плавающей запятой IEEE 754.
Сначала мы опишем, как представляются числа с плавающей запятой. Java использует подмножество двоичного стандарта с плавающей запятой IEEE 754 для представления чисел с плавающей запятой и определения результатов арифметических операций. Практически все современные компьютеры соответствуют этому стандарту. float представлен 32 битами, и каждая возможная комбинация битов представляет одно действительное число. Это означает, что можно точно представить не более 2 32 возможных действительных чисел, даже если существует бесконечно много действительных чисел (даже от 0 до 1). Стандарт IEEE использует внутреннее представление, подобное экспоненциальному представлению, но в двоичном формате вместо основания 10. Оно охватывает диапазон от ±1,40129846432481707e-45 до ±3,40282346638528860e+38. с 6 или 7 значащими десятичными цифрами, включая плюс бесконечность, минус бесконечность и NaN (не число). Число содержит знаковый бит s (интерпретируемый как плюс или минус), 8 бит для экспоненты e и 23 бита для мантиссы M. . Десятичное число представляется в соответствии со следующей формулой.
- Бит знака s (бит 31). Старший бит представляет знак числа (1 для отрицательного значения, 0 для положительного).
- Поле экспоненты e (биты 30–23). Следующие 8 бит представляют экспоненту. По соглашению показатель степени смещен на 127. Это означает, что для представления двоичного показателя степени 5 мы кодируем 127 + 5 = 132 в двоичном виде (10000100). Чтобы представить двоичный показатель степени -5, мы кодируем 127 - 5 = 122 в двоичном формате (01111010). Это соглашение является альтернативой записи с дополнением до двух для представления отрицательных целых чисел.
- Мантисса m (биты 22–0). Остальные 23 бита представляют собой мантиссу, нормализованную в диапазоне от 0,5 до 1. Эта нормализация всегда возможна путем настройки двоичный показатель соответственно. Двоичные дроби работают так же, как десятичные дроби: 0,1101 представляет собой 1/2 + 1/4 + 1/16 = 13/16 = 0,8125. Не каждое десятичное число можно представить в виде двоичной дроби. Например, 1/10 = 1/16 + 1/32 + 1/256 + 1/512 + 1/4096 + 1/8192 + . В этом случае число 0,1 аппроксимируется ближайшей 23-битной двоичной дробью 0,000110011001100110011. Применяется еще одна оптимизация.Поскольку мантисса всегда начинается с 1, нет необходимости явно сохранять этот скрытый бит.
Например, десятичное число 0,085 хранится как 00111101101011100001010001111011.
Это точно представляет число 2 e-127 (1 + m / 2 23 ) = 2 -4 (1 + 3019899/8388608) = 11408507/134217728 = 0,085000000894069671630859375.
double похож на float, за исключением того, что его внутреннее представление использует 64 бита, 11-битную экспоненту со смещением 1023 и 52-битную мантиссу. Это охватывает диапазон от ±4,94065645841246544e-324 до ±1,79769313486231570e+308 с точностью до 14 или 15 значащих цифр.
Точность и точность.
Точность = точность спецификации. Точность = правильность. Не путайте точность с точностью. 3,133333333 — это оценка математической константы π, которая указана с точностью до 10 знаков после запятой, но имеет точность только до двух знаков после запятой. Как однажды сказал Джон фон Нейман: «Нет смысла быть точным, если вы даже не знаете, о чем говорите». Java обычно выводит числа с плавающей запятой с точностью до 16 или 17 знаков после запятой, но не верьте слепо, что это означает такое количество цифр точности! Калькуляторы обычно отображают 10 цифр, но вычисляют с точностью до 13 цифр. Кахан: зеркало космического телескопа Хаббл было отшлифовано с большой точностью, но с неправильными характеристиками. Следовательно, изначально это был большой провал, поскольку он не мог создавать изображения с высоким разрешением, как ожидалось. Однако его точность позволила астронавту установить корректирующую линзу, чтобы сбалансировать ошибку. Валютные расчеты часто определяются в терминах заданной точности, например, обменные курсы евро должны быть указаны с точностью до 6 цифр.
Ошибка округления.
- Округление десятичных дробей. Например, первый фрагмент кода ниже из FloatingPoint.java выводит false, а второй выводит true .
Действительно, для некоторых значений c этот метод работает.
Это может дать нам некоторую уверенность в правильности нашего фрагмента кода. Но когда мы пытаемся вычислить квадратный корень из 20, происходит удивительная вещь. Наша программа застревает в бесконечном цикле! 2 == c), а скорее потому, что (t 2 Этот тип ошибки называется ошибкой округления. Машинная точность — это наименьшее число ε, такое что (1,0 + ε != 1,0 ). В Java это XYZ с double и XYZ с float. Изменение допуска ошибки ε на небольшое положительное значение помогает, но не устраняет проблему (см. упражнение XYZ).
Нам нужно довольствоваться аппроксимацией квадратного корня. Надежный способ выполнения вычислений состоит в том, чтобы выбрать некоторую допустимую погрешность ε, скажем, 1E-15, и попытаться найти значение t такое, что |t - c/t| Многие численные вычисления (например, интегрирование или решение дифференциальных уравнений) включают суммирование множества мелких членов. Ошибки могут накапливаться. Такое накопление ошибки округления может привести к серьезным проблемам.
Каждый раз, когда вы выполняете арифметическую операцию, вы вносите дополнительную ошибку не менее ε. Керниган и Плаугер: «Числа с плавающей запятой подобны кучам песка: каждый раз, когда вы перемещаете одну из них, вы теряете немного песка и поднимаете немного грязи». Если ошибки возникают случайным образом, мы можем ожидать суммарную ошибку sqrt(N) εm. Однако, если мы не проявляем бдительности при разработке наших численных алгоритмов, эти ошибки могут распространяться очень неблагоприятными и неинтуитивными способами, приводя к кумулятивным ошибкам N εm или хуже.
Финансовые вычисления.
- Налог с продаж. Программа Financial.java иллюстрирует опасность. Рассчитайте налог с продаж в размере 9% от телефонного звонка за 50 центов. (или 5% налога с продаж на телефонный звонок в размере 0,70 доллара США). Используя IEEE 754, 1,09 * 50 = xxx. Этот результат округляется до 0,xx, хотя точный ответ равен 0,xx, который телефонная компания (по закону) обязана округлить до 0,xx (используя округление Banker). Напротив, налог в размере 14% на телефонный звонок стоимостью 75 центов дает xxx. может быть округлен до x.xx, даже если (по закону) телефонная компания должна округлить число (используя округление Banker) до x.xx. Эта разница в копейках может показаться незначительной, и вы можете надеяться, что в долгосрочной перспективе эти эффекты компенсируют друг друга. Однако, если учесть сотни миллионов транзакций, эта нарезка салями может принести миллионы долларов. Ссылка: Супермен III (1983 г.), Хакеры (1995 г.) и Офисное пространство (1999 г.). В Office Space трое друзей заражают учетную систему компьютерным вирусом, округляющим доли цента в меньшую сторону, и переводят его на свой счет.
что ведет к 1051.2674964674473. Вместо этого предположим, что банк хранит ваш баланс в виде целого числа (измеряемого в пенни). В конце каждого дня банк подсчитывает ваш баланс, умножает его на 1,05 и округляет результат до копейки. Тогда у вас останется всего 1051 доллар.10, и были обмануты из 17 центов. Предположим, вместо этого банк округляет в меньшую сторону до копейки в конце каждого дня. Теперь у вас останется всего 1049,40 долларов, и вы будете обмануты на 1,87 доллара. Ошибка не хранения долей копейки накапливается и со временем может стать существенной, а то и мошеннической. Программа CompoundInterest.java.
Другой источник ошибки.
В дополнение к ошибке округления, присущей арифметике с плавающей запятой, существуют некоторые другие типы ошибок аппроксимации, которые обычно возникают в научных приложениях.
Катастрофическая отмена.
Разрушительная потеря точности, когда маленькие числа вычисляются из больших чисел путем сложения или вычитания. Например, если x и y совпадают во всех, кроме последних нескольких цифрах, то если мы вычислим z = x - y, то z может иметь точность только в несколько цифр. Если мы впоследствии используем z в расчетах, то точность результата может составлять всего несколько цифр.
-
Построение графика функции. Попробуйте построить график f(x) = (1 - cos x) / (x^2) от x = -4 * 10^-8 до 4 * 10^-8. В этой области математическая функция f(x) приблизительно постоянна со значением 0,5. Однако в IEEE с плавающей запятой это определенно не так! Программа Catastrophic.java делает это и дает очень неожиданные результаты.
Численный анализ.
- Стабильность. Математическая задача называется хорошо обусловленной, если ее решение изменяется лишь на небольшую величину при незначительном изменении входных параметров. Алгоритм является численно устойчивым, если выходные данные алгоритма изменяются лишь на небольшую величину, когда входные данные изменяются на небольшую величину. Численная стабильность показывает, как ошибки распространяются алгоритмом. Численный анализ — это искусство и наука поиска численно устойчивых алгоритмов для решения хорошо обусловленных задач. Точность зависит от условий задачи и устойчивости алгоритма. Неточность может быть результатом применения стабильного алгоритма к плохо обусловленной задаче или нестабильного алгоритма к хорошо обусловленной задаче.
- Экспоненциальная функция. В качестве простого примера вычисление f(x) = exp(x) является хорошо обусловленной задачей, поскольку f(x + ε) = . Один из алгоритмов вычисления exp(x) основан на использовании ряда Тейлора. Предположим, мы оцениваем f(x) = exp(x), используя первые четыре члена его тейлоровской аппроксимации: g(x) = 1 + x + x 2 /2 + x 3 /3!. Тогда f(1) = 2,718282, g(1) = 2,666667. Однако он нестабилен, так как если x -x использовать ряд Тейлора и взять обратную величину.
- Функция (1 - cos x) / (x^2). Укажите стабильный способ вычисления этой функции.
Сложение, умножение, возведение в степень и деление положительных чисел — все это хорошо обусловленные задачи. То же самое и с вычислением корней квадратного уравнения. (См. упражнение XYZ.) Вычитание плохо обусловлено. То же самое и с нахождением корней общего многочлена. Обусловленность задачи решения Ax = b зависит от матрицы A.
Динамика численности населения. Уравнение Ферхюльста представляет собой упрощенную модель динамики численности населения.
Программа Verhulst.java считывает параметр командной строки R и повторяет уравнение Ферхюльста в течение 100 итераций, начиная с x0 = 0,5. Он упорядочивает вычисления четырьмя различными, но математически эквивалентными способами. Все они приводят к существенно разным результатам при R = 3, ни один из которых не является правильным (что можно проверить с помощью точной арифметики в Maple). (Ссылка)
Ответ: девять (пять в первом цикле и четыре во втором цикле). Результат:
Читайте также: