Pull to refresh

Comments 45

Реально десятичные вещественные реализованы в z/Architecture. Основная польза от них, как представляется, не для научных расчётов, а для финансов -- раньше там использовались тоже десятичные числа, но целые переменной длины (1-31 цифра плюс знак), что было реализовано ещё в Системе 360 и благополучно дожило до наших дней.

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

В общем, в той же z/Architecture двоичные и десятичные будут благополучно сосуществовать и дальше, а вот внедрение десятичных (хоть целых, хоть вещественных) в другие архитектуры представляется довольно сомнительным, не смотря на определённые выгоды от них в некоторых задачах.

А в финансах именно десятичные вещественные с мантиссой/порядком (когда мы, соответственно, теряем целые рубли, оперируя суммами 10^много рублей) действительно нужны? На взгляд дилетанта - там либо операции с суммами на счетах, налогами и т.п., когда нужна точность до копеек независимо от суммы, либо аналитика (просчитать какие-нибудь тренды), когда можно округлить и "по двоичному".

Исторически для финансов используются целые BCD (точней, с фиксированной запятой; целые -- их частный случай). Конкретно на z/Architecture преимущество от вещественных десятичных может быть в том, что их можно хранить в регистрах процессора, а обычные целые десятичные всегда хранятся только в памяти и имеют переменную длину; соответственно, обрабатывать вещественные целые можно быстрей. Но, чтоб не было недопустимых округлений, придётся постоянно следить за порядком и не допускать его выхода за определённые пределы. С целыми в памяти в этом смысле проще: если результат не лезет в отведённое поле, автоматом фиксируется переполнение. Как обстоит с этим дело на практике, не скажу: к финансовому сектору отношения не имею и, надеюсь, иметь не буду :)

При современных объемах хранения данных, копейки или центы в формате int64 решают все практические задачи в финансах. Объем нормативных и прочих документов, хранимых в центробанке, явно на много порядков превышает объем всех финансовых транзакций, так что мало проку экономить на битах в числах.

Ну, с появлением 64-разрядных целых появились и шутки про способность компьютера считать госдолг США :) Если же серьёзно, считать числа необходимой разрядности можно было на Системе 360 (собственно, и считали), но это делалось без лишнего геморроя лишь в десятичных операциях: двоичных операций над 64-разрядными числами не было (сама архитектура 32-разрядная). Для реализации 64-разрядных операций сложения-вычитания требовалась последовательность команд типа "сложение - условный переход - сложение" (перенос и переполнение выявлялись, но не могли прямо использоваться арифметическими командами). С умножением и делением, понятное дело, ещё хуже.

копейки или центы в формате int64 решают все практические задачи в финансах

Случаи разные бывают. Скажем, ВВП Югославии в 1993ем был в районе 100 миллиардов долларов США, доллар 31 декабря 1993го стоил 1,775,998,646,615 динаров - так что на работу с суммами порядка ВВП в динарах, если ничего не путаю, фиксированных 64 бит не хватило бы. Не факт, что подобное не повторится.

Если не ошибаюсь, для выражения ВВП в динарах потребовалось бы 22 десятичных разряда -- если б и влезло в 64-разр целое, то на пределе. А вот десятичные переменной длины Системы 360 справились бы -- там до 31 цифры включительно :)

Нет, сейчас посмотрел даже в 8,1 нет единицы в Ieee754, так что я ошибся, с binary всё ещё хуже

Вот только какое отношение пиковый курс доллара имеет к годовому ввп в местной валюте? А по среднему курсу за год ВВП без проблем считается. При желании, можно придумать и ситуацию, когда экспоненты не хватит, и даже не факт, что где-то когда-то это не произойдет - так вы предлагаете всем заранее отказаться от экономики и математики? :)

У топовых предприятий обороты порядка ВВП страны, и налоги и прочее им надо считать в местной валюте - так что при составлении отчётности за 1993 год надо было оперировать подобными суммами.

предлагаете всем заранее отказаться от экономики и математики?

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

Пиковый курс на несколько минут или часов никак не может повлиять на годовую отчетность, о чем вы? Там все скачки курса заняли пару дней, что никак не влияет.

Да, соглашусь, возможно в отдельной компании проблем не было. Но всё таки двенадцатизначный курс там держался неделю и всё это время в ЦБ надо было с точностью до динара рассчитывать имеющиеся ресурсы включая золотовалютные запасы.

В числе 1 + 1e-16, 16 цифр совпадают, как и должно быть, но, например, в числе 8,1 binary64 начнёт писать 8,09999…, тому, что нет даже единицы я был удивлён, а если эти числа начать складывать… - Отдам голос за decimal64

А x86 тоже до сих пор есть десятичная арифметика.

Изначально в 8086/8088 (16-разрядных ещё, не 32) были команды коррекции для обычных арифметических операций. Это -- не полноценная десятичная арифметика, какая имеется на ИБМовских мэйнфреймах, это -- "жалкое подобие левой руки" (с), родом из калькуляторных применений. Но с переходом на 64 бита и её полностью выпилили вместе с рядом других команд. Так что современные процы могут выполнять эти команды только в 16- и 32-разрядных режимах, где они сохраняются для совместимости.

От них толку было примерно 0: они крайне неэффективны, почему и не использовались. Если хотите реально разобраться -- учите ассемблер и пробуйте написать полноценную десятичную арифметику а-ля мэйнфреймы на нём :)

Лично мне не известны применения десятичных чисел, кроме как в финансово-экономических задачах. Любые научно-технические расчёты (а всякие 3D-модели и т.п. к ним и сводятся) ведутся исключительно в двоичном виде: там нет жёстких правил, заточенных именно на десятичную систему, как в финансах, и достаточно лишь обеспечить необходимую точность результата, а это можно сделать в любой системе.

Лично мне не известны применения десятичных чисел, кроме как в финансово-экономических задачах.

Cascading Style Sheets:

.main {
  box-sizing: border-box;
  float: left;
  width: 70%;
  background-color: #fff;
  border-radius: .5em;
}

Любые научно-технические расчёты (а всякие 3D-модели и т.п. к ним и сводятся) ведутся исключительно в двоичном виде.

Расчёты, может и ведутся в двоичном виде, так как процессор, например, троичную арифметику, вряд ли поддерживает. Однако хранение исходных данных может быть необходимо в десятичных числах. Хватает текстовых 3D-форматов, например OBJ. Его можно преобразовать при считывании в более компактный формат, а затем, после изменений, сохранить в текстовом виде без ошибок преобразования десятичных чисел.

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

в десятичном -- только текстовые по своей сути файлы, пример которого Вы и приводите

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

всё начинает считаться в двоичном виде

Здесь может быть путаница с терминологией. Потому что, как, например, назвать расчёты в рациональных числах? Двоичные вычисления? Если я правильно помню, калькулятор, встроенный в Windows, вычисляет в рациональных числах. Десятичные числа - это частный случай рациональных чисел.

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

Может, вы и правы, что только экономисты это используют. Помню, когда-то давно впервые стал знакомиться с 3D-редактором Cinema 4D. И вскоре обнаружил, что этот редактор отказывается сохранять те десятичные числа, что я ввожу. Вместо короткого числа - выводит какую-то длиннющую дробь с кучей девяток. Видимо, они пожадничали с размером чисел с плавающей точкой. После этого я забросил ту программу.

Здесь может быть путаница с терминологией. Потому что, как, например, назвать расчёты в рациональных числах? Двоичные вычисления? Если я правильно помню, калькулятор, встроенный в Windows, вычисляет в рациональных числах. Десятичные числа - это частный случай рациональных чисел.

Ну, вообще, насколько помню, рациональные -- часть действительных чисел (к которым ещё иррациональные относятся); к системе счисления это прямого отношения не имеет.

Когда я говорю о вычислениях в десятичном или двоичном виде, я говорю именно о системе счисления, используемой в процессе расчётов -- не о характере чисел как таковых. Сама цифровая электроника работает только с двоичными числами (не считая троичной советской "Сетуни"); для выполнения не двоичных, а десятичных вычислений (не путать с отображением на экране! речь про внутренние операции) каждая десятичная цифра кодируется четырьмя битами -- из 16 возможных значений используются первые 10 (для цифр 0-9), а правила выполнения операций над полученной двоичной записью истинно десятичного числа (так называемые двоично-десятичные числа -- BCD, binary coded decimal) отличаются от обработки "настоящих" двоичных чисел.

Например, если мы складываем двоичные числа 1001 и 0001 (соответственно 9 и 1 в десятичном виде), то получаем двоичное число 1010 (при переводе из двоичной в десятичную -- 10), а вот если мы те же самые числа 1001 и 0001 трактуем как BCD, то в результате сложения получим 0001 0000 -- т.е. тоже 10, но в двоично-десятичном, а не десятичном виде. По сути, с помощью BCD на двоичном "железе" имитируются вычисления в десятичной системе -- но места под их хранение надо больше, о чём говорилось в статье выше.

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

Нет, они не пожадничали, просто многие десятичные числа не представимы точно в двоичном виде. Они отвели либо стандартный float, либо стандартный double, но просто не стали округлять отображаемое значение до "приятного глазу" вида. Любые другие программы хранят вещественные числа точно так же, и если Вы вводите куда-то 0.1 и видите на экране 0.1, это означает лишь округление при отображении; в памяти всё равно хранится величина, чуть большая десятичного 0.1 -- максимально близкое его двоичное представление.

Вот, например, иллюстрация того, как десятичное 0.1 представляется в виде двоичного вещественного числа и чему равно в десятичном виде его истинное значение:

Отсюда хорошо видно, что десятичное значение 0.1 не может быть точно представлено в двоичном вещественном виде, так что если претензии к программистам Cinema 4D и предъявлять, то только в том, что они поленились сделать "красивое" округление.

Сохранить точное 0.1 можно в текстовом представлении, как иногда и делается. Но машина-то будет обрабатывать числа не в текстовом виде, а сначала переведёт в двоичное, так что любые вычисления будут использовать не 0.1, а 0.100000001490116119384765625 -- просто пользователь не всегда это видит.

рациональные -- часть действительных чисел

Я имел в виду обыкновенные дроби. Они хранятся в виде пары значений: числитель и знаменатель.

так что если претензии к программистам Cinema 4D и предъявлять, то только в том, что они поленились сделать "красивое" округление.

Они отвели недостаточно места для хранения числа. Причём, если бы они для хранения чисел, введённых пользователем, использовали десятичные числа, а не торопились их преобразовать в двоичные, то 4 байт им вполне могло бы хватить для точного хранения тех значений, что я вводил.

Я имел в виду обыкновенные дроби. Они хранятся в виде пары значений: числитель и знаменатель

Да, так можно делать, но делают крайне редко: это сильно усложняет программирование и убивает производительность.

Они отвели недостаточно места для хранения числа. Причём, если бы они для хранения чисел, введённых пользователем, использовали десятичные числа, а не торопились их преобразовать в двоичные, то 4 байт им вполне могло бы хватить для точного хранения тех значений, что я вводил

Они отвели столько места, сколько поддерживает для внутреннего представления чисел аппаратура -- либо 4, либо 8 байтов (типы float и double соответственно). Если бы они сделали так, как предлагаете Вы, производительность упала бы в десятки, сотни, а то и тысячи раз -- и поэтому так поступают только при крайней нужде, когда иначе вообще никак, а нужно. Но в реальной жизни никакой нужды в точном представлении подавляющего большинства значений попросту нет, и поэтому используют то, что удобнее и быстрее работает -- просто некоторые программы обманывают пользователей, рисуя на экране "красивые" значения вместо реальных, а другие показывают, что есть в действительности.

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

Двоично-десятичная система не является "более точной". В десятичной системе тоже легко получить бесконечно длинные дробные части (в периоде и без). Сохранение введенного вручную числа (любимое 0.1) не является признаком "точности". Уж если такая задача реально возникает, то она легко исправляется другими методами. Например, округлением до нужного знака после запятой, а это уже больше связано с алгоритмом преобразования двочного представления в десятичное.

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

В числе 1 + 1e-16, 16 цифр совпадают, как и должно быть, но, например, в числе 8,1 binary64 начнёт писать 8,09999…, тому, что нет даже единицы я был удивлён, а если эти числа начать складывать… - Отдам голос за decimal64

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

Нет, это вполне может приводить к потере точности. Операции-то выполняются одна за другой, и каждая из них потенциально даёт результат, чуть-чуть отличающийся от точного -- причём отличающегося не так, как он отличался бы при расчётах в десятичной системе. Такие мелкие ошибки могут накапливаться, и итоговый результат может не совпасть с требуемым по строгим правилам. Никаким округлением это не исправить.

Эээ, а ничего, что когда-то на счетах справлялись без потери точности? Кстати, тогда еще доли копеек в ходу были, справлялись и с ними.

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

Вы не понимаете принцип - как на счетах работали с дробями? Как выше писали, десятичные дроби тоже бывают бесконечными, но на счетах это решили (как?).

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

Как? Минимальный шаг между числами в десятичной системе намного больше, чем в двоичной для одного знака в пять раз, для двух в 25 раз и т.д.. А здесь Decimal даже точнее будет

Вы неправильно понимаете принцип двоично-десятичного представления. на один десятичный разряд приходится 4 бита, а не один. Для упомянутой в статье кодировке Чен-Хо на 3 десятичных разряда тратися 10 бит (т.е. 1000 vs 1024). Двоично-десятичное представление рыхлое - содержит неиспользуемые значения на всём возможном диапазоне.

Я сейчас статью процитирую: «Потери были хоть какими-то в этом случае только для 10 байтов: 1000 против 1024 десятичных цифр, но покажите мне реальное применение таких чисел с 80 битами в мантиссе(!).». Даже для 128 бит разницы нет, но зато мы получаем более точную арифметику.

Можно и не накапливать результат.

10.2 * 3 >= 30.6

возвращает false при любой точности binary float

Было бы лучше, если double и float, например, всегда выдавали результат чуть в большую сторону, но для 0,1 они выдают число больше 0,1, а, например, для 8,1 меньше 8,1, это приводит к тому, что после сложения этих двух чисел появляются двойки в первых 16(отсюда и эта ошибка). Это ещё один плюс decimal64 в плане точности и экспонента больше, но поддержки Decimal64 нет.

На выводе округлится, но в аппаратном плане округлить нельзя, а значит до свидания даже 16 десятичных цифр

Красивое округление приведет к непреднамеренной потере копеек.

Десятичные числа нужны не только для финансистов. Ещё они нужны для графической разметки. Описание разметки задаётся примерно в виде: "умножить высоту этого окна на 80% и сложить с удвоенной толщиной этой рамки". Если мы преобразовали десятичные числа в двоичные, и прокручиваем длинный документ, то ошибка накапливается, пока графические элементы не начнут съезжать от правильных положений. Всё, что касается разметки, 3D-графики, чертежей - там нужны десятичные числа.

Sign up to leave a comment.

Articles