Почему компьютеры не хранят десятичные числа как второе целое число?

24

Компьютеры имеют проблемы с хранением дробных чисел, где знаменатель - это нечто иное, чем решение 2 ^ x. Это потому, что первая цифра после десятичной дроби стоит 1/2, вторая 1/4 (или 1 / (2 ^ 1) и 1 / (2 ^ 2)) и т. Д.

Зачем иметь дело со всевозможными ошибками округления, когда компьютер мог просто сохранить десятичную часть числа как еще одно целое число (что является точным?)

Единственное, о чем я могу думать, это иметь дело с повторяющимися десятичными числами (в базе 10), но это могло бы быть крайним решением (как у нас сейчас с бесконечностью).

SomeKittens
источник
8
Вы должны посмотреть, как хранятся десятичные типы, в отличие от типов с плавающей запятой / типа double.
Одед
9
Не знаю, как это точнее. Первая цифра после десятичной дроби - 1/10, вторая - 1/100 и т. Д. Насколько точнее все еще возникают проблемы с округлением (как вы представляете 1/3)? Разница лишь в том, какие значения могут быть представлены точно.
Мартин Йорк,
17
Десятичная с плавающей запятой (то, что вы имеете в виду два, просто в более неуклюжем представлении) не более неточна, чем двоичная с плавающей запятой. Единственная разница в том, какие значения не могут быть представлены, и поскольку мы привыкли к десятичной системе, мы не замечаем ошибок десятичной версии. И нет, ни один не может представлять все рациональные и иррациональные числа.
1
В конце концов, все сводится к эффективности. Компьютеры являются двоичными, и схемы для работы с этим двоичным представлением гораздо менее сложны. Важность этого может быть несколько уменьшена сегодня, но это было время, когда это было очень важно. Также любое представление, которое вы выберете для хранения своего числа (в конечном пространстве) на компьютере, будет иметь конечный набор значений, которые оно может представлять, и все они будут иметь ошибки округления с некоторыми входными данными. Типичный формат с плавающей точкой с Mantissa и Exponent предлагает гораздо больший диапазон, чем было бы возможно при использовании двух целых чисел.
Мистер Миндор
1
Я настоятельно рекомендую прочитать некоторые статьи, на которые есть ссылки в моем ответе на вопрос « Что вызывает ошибки округления с плавающей запятой»? который я только что обновил с деталями последней статьи в ссылочной серии. В частности, посмотрите, почему Fixed Point не излечит ваш блюз с плавающей точкой .
Марк Бут

Ответы:

35

Есть на самом деле режимы чисел, которые делают это.

В двоичной арифметике десятичных (BCD) компьютер работает на базе 10. Причина, по которой вы редко сталкиваетесь с этим, заключается в том, что она тратит пространство: каждая отдельная цифра числа занимает минимум четыре бита, в то время как компьютер может хранить до 16 значений в этом пространстве. (Это также может быть медленнее, но возможно иметь математику BCD с аппаратным ускорением, которая работает просто отлично.). Фактически это именно то, что делает большинство калькуляторов, поэтому существуют определенные классы проблем с округлением, которые вы никогда не столкнетесь с Casio за 5 долларов, который съест ваш обед на настольном компьютере.

Другой способ, которым вы можете воспользоваться, - это использовать рациональные числа, то есть числитель и знаменатель, хранящиеся в виде целых чисел. Это на самом деле доступно почти на всех языках, является точным и позволяет хранить все в собственных двоичных форматах. Проблема в том, что, в конце концов, пользователи, вероятно, не хотят видеть такие дроби, как 463/13 или даже 35 и 8/13. Они хотят видеть 35.615 ... и как только вы туда доберетесь, вы столкнетесь со всеми типичными проблемами. Добавьте к этому, что этот формат занимает даже больше места и может быть значительно медленнее, чем арифметика с плавающей запятой, и вы не обнаружите, чтобы компьютеры использовали этот формат по умолчанию.

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

Бенджамин Поллак
источник
Разве вы не имеете в виду четыре бита (не байты) в абзаце BCD?
3
Другой вариант - арифметика с фиксированной запятой, где целое число представляет десятичную дробь, если число - например, Хранение денежных значений (без вычислений с использованием десятичных дробей или процентов), где 1 представляет 0,01 доллара США.
Mattnz
1
@mattnz: True - фиксированные точки - это особый случай рациональных чисел.
Джон Перди
Удивительно, не знал, что калькуляторы это сделали.
SomeKittens
3
Есть третий вариант. Плавающая точка с десятичной экспонентой, например, как decimalреализован C # : stackoverflow.com/a/5019178/174335 Это не BCD, так как нет индивидуального представления десятичных цифр и это не фиксированная точка.
Джорен
38

Существует множество способов хранения дробных чисел, и каждый из них имеет свои преимущества и недостатки.

Плавающая точка , безусловно, самый популярный формат. Он работает, кодируя знак, мантиссу и подписанный показатель base-2 в целые числа и упаковывая их в кучу битов. Например, вы можете иметь 32-битную мантиссу 0.5(кодированную как 0x88888888) и 32-битную подписанную экспоненту +3( 0x00000003), которая будет декодировать в 4.0(0.5 * 2 ^ 3). Числа с плавающей запятой являются быстрыми, потому что они реализованы аппаратно, и их точность масштабируется с абсолютным размером, то есть, чем меньше число, тем выше абсолютная точность, поэтому относительная ошибка округления остается постоянной с абсолютным размером. Число с плавающей запятой отлично подходит для значений, взятых из непрерывной области, таких как длины, уровни звукового давления, уровни освещенности и т. Д., И поэтому они обычно используются при обработке звука и изображений, а также для статистического анализа и моделирования физики. Их самым большим недостатком является то, что они не являются точными, то есть они склонны к ошибкам округления и не могут точно представлять все десятичные дроби. Все основные языки программирования имеют некоторую точку плавания.

Фиксированная точкаработает с использованием достаточно больших целых чисел и неявно резервирует часть их битов для дробной части. Например, 24,8-битное число с фиксированной точкой резервирует 24 бита для целой части (включая знак) и 8 бит для дробной части. Сдвиг вправо этого числа на 8 битов дает нам целую часть. Числа с фиксированной запятой были популярны, когда аппаратные единицы с плавающей запятой были необычны или, по крайней мере, намного медленнее, чем их целочисленные аналоги. В то время как числа с фиксированной запятой несколько легче обрабатывать с точки зрения точности (хотя бы потому, что их легче рассуждать), они уступают числам с плавающей точкой практически во всех других отношениях - они имеют меньшую точность, меньший диапазон и потому, что дополнительные Операции необходимы для корректировки вычислений для неявного сдвига, сегодня математика с фиксированной запятой часто медленнее, чем математика с плавающей запятой.

Десятичные типы работают во многом как числа с плавающей запятой или числа с фиксированной запятой, но они принимают десятичную систему, то есть их показатель степени (неявный или явный) кодирует степень-10, а не степень-2. Десятичное число может, например, кодировать мантиссу 23456и показатель степени -2, и это расширится до234.56, Десятичные числа, поскольку арифметика не встроена в процессор, медленнее, чем числа с плавающей запятой, но они идеальны для всего, что включает десятичные числа и требует, чтобы эти числа были точными, с округлением, происходящим в четко определенных местах - финансовые вычисления, табло и т. д. Некоторые языки программирования имеют встроенные десятичные типы (например, C #), другим требуются библиотеки для их реализации. Обратите внимание, что хотя десятичные числа могут точно представлять неповторяющиеся десятичные дроби, их точность не лучше точности чисел с плавающей запятой; выбор десятичных дробей просто означает, что вы получаете точные представления чисел, которые могут быть представлены точно в десятичной системе (точно так же, как числа с плавающей точкой могут точно представлять двоичные дроби).

Рациональные числа хранят числитель и денумератор, обычно используя некоторый тип целочисленного типа bignum (числовой тип, который может расти настолько, насколько позволяют ограничения памяти компьютера). Это единственный тип данных из множества, который может точно моделировать числа, такие как 1/3или 3/17, а также операции с ними - рациональные, в отличие от других типов данных, будут давать правильные результаты для таких вещей, как3 * 1/3, Математика довольно проста, хотя придумать эффективный алгоритм факторинга довольно сложно. Некоторые языки программирования имеют встроенные рациональные типы (например, Common Lisp). Недостатки рационального подхода заключаются в том, что они медленны (многие операции требуют сокращения дробей и факторинга их компонентов) и что многие обычные операции трудно или невозможно реализовать, и большинство реализаций приведут рациональное к плавающему, когда это произойдет (например, когда вы вызываете sin()по рациональному).

BCD (Binary Coded Decimal) использует «полубайты» (группы по 4 бита) для кодирования отдельных цифр; так как клев может содержать 16 различных значений, но десятичные числа требуют только 10, для каждого куска есть 6 «недопустимых» значений. Как и десятичные числа, числа BCD являются точными десятичными числами, то есть вычисления, выполняемые для десятичных чисел, работают так же, как если бы вы делали их с помощью ручки и бумаги. Арифметические правила для BCD несколько неуклюжи, но есть и преимущество в том, что преобразовать их в строки проще, чем в некоторых других форматах, что особенно интересно для сред с ограниченными ресурсами, таких как встроенные системы.

Строки , да, простые старые строки, также могут использоваться для представления дробных чисел. Технически, это очень похоже на BCD, только с явной десятичной точкой, и вы используете один полный байт на десятичную цифру. Таким образом, формат является расточительным (используются только 11 из 256 возможных значений), но его легче анализировать и генерировать, чем BCD. Кроме того, поскольку все используемые значения являются «не подозрительными», безвредными и не зависящими от платформы, строки в кодировке строк могут без проблем перемещаться по сетям. Нередко найти арифметику, выполняемую непосредственно над строками, но это возможно, и когда вы делаете это, они точно так же десятичны, как и другие десятичные форматы (десятичные дроби и BCD).

tdammers
источник
Конечно, 32-битная фиксированная точка имеет большую точность, чем 32-битная с плавающей точкой, поскольку представления с фиксированной точкой не включают в себя мантиссу.
хань
4
@han: зависит от размера номера, который вы хотите сохранить. Число с плавающей точкой (приблизительно) даст вам одинаковую точность независимо от того, насколько большое или маленькое число, в то время как фиксированная точка даст вам полную точность, только если число, которое вы хотите сохранить, идеально вписывается в его диапазон.
Лев
@han Не обязательно, оба могут представлять 2 ^ 32 различных значения. Количество передаваемой информации одинаково, независимо от представления. Диапазон и точность идут рука об руку, поэтому в этом отношении арифметика с фиксированной точкой может быть более точной в определенных диапазонах. И избегает неприятных случайных проблем округления, если вы знаете пределы, в пределах которых вы можете работать.
zxcdw
@ Хан: они имеют одинаковую точность (или почти). Разница заключается в том, что для чисел с фиксированной запятой точность (как в размере дискретного шага от одного числа до его преемника) постоянна, как и с целыми числами, тогда как с помощью чисел с плавающей точкой она растет примерно линейно с абсолютным значением - с плавающей точкой число 1.0 имеет большую точность, чем число 10 000 000,0 (примерно в миллион раз больше).
tdammers
6

Числа с плавающей запятой представляют широкий диапазон значений, что очень полезно, когда вы заранее не знаете, какими могут быть значения, но это компромисс. Представление 1/10 ^ 100 со вторым целым числом не сработает.

Некоторые языки (и некоторые библиотеки) имеют другие характеристики. Лисп традиционно имеет бесконечные целые числа точности. Кобол имеет вычисления с десятичными числами с фиксированной запятой.

Вы должны выбрать свое числовое представление, соответствующее проблемной области.

ddyer
источник
1

Похоже, вы описываете числа с фиксированной точкой .

Имейте в виду, что хранение дробной части числа в отдельном месте точно идентично созданию одного пробела, вдвое длиннее, и сохранению целой и дробной части в двух отдельных половинах. Другими словами, это то же самое, что хранить число в виде целого числа, но просто предполагать фиксированное количество десятичных пробелов.

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

tylerl
источник
1

Это будет называться BCD, я думаю, что вы все еще можете использовать его, если вы действительно хотите. Однако это не стоит того, чтобы:

  1. Вы очень редко столкнетесь с ошибкой округления с 64-битной плавающей точкой
  2. Это делает арифметическое сложным и неэффективным
  3. Это тратит 6 значений каждые 4 бита
Перевернутая лама
источник
BCD математика часто использовалась в ранних 8-битных микропроцессорных системах; действительно, на одном популярном микропроцессоре (6502) сложение и вычитание с помощью BCD выполняются так же быстро, как и с двоичными данными. Видеоигры часто использовали математику BCD для ведения счетов. Там нет специальной обработки для оборачивания баллов в 1 000 000 баллов. Вместо этого добавление 1 к «99 99 99» приводит к «00 00 00» с переносом, который игнорируется. Дополнительные затраты на добавление оценок в BCD незначительны по сравнению со стоимостью преобразования двоичного значения в отображаемый формат.
суперкат
1

Короткий ответ: плавающая точка была разработана для научных расчетов. Он может хранить число с (до) заданным числом значащих цифр, что близко соответствует тому, как точность измеряется в большинстве научных расчетов.

Это, как правило, поддерживается аппаратными средствами в основном потому, что научные расчеты, как правило, наиболее выигрывают от аппаратной поддержки. Например, финансовые расчеты часто выполняются с использованием других форматов, но финансовое программное обеспечение обычно мало выполняет реальных вычислений, поэтому, хотя необходимые форматы поддерживаются только программным обеспечением, производительность остается совершенно адекватной для большинства финансовых программ.

Джерри Гроб
источник