Среди всего разнообразия форматов представления действительных чисел в компьютерной технике особое место отведено формату чисел с плавающей точкой (ЧПТ), который запротоколирован в стандарте IEEE754. Главные достоинства чисел с плавающей точкой, как известно, заключаются в том, что они позволяют производить вычисления в большом диапазоне значений и при этом вычисления организуются инструментарием двоичной арифметики, легко реализуемой на вычислительном устройстве. Однако последнее обстоятельство таит в себе подводные камни, которые являются причиной того, что расчеты, сделанные с использованием этого формата, могут приводить к совершенно непредвиденным результатам.

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

В настоящей статье мы не приводим теоретических выкладок, которые объясняют причину появления этих ошибок. Это тема следующего топика. Здесь мы только постараемся привлечь внимание специалистов к проблеме катастрофической неточности вычислений, возникающей при проведении арифметических операций над десятичными числами при использовании двоичной арифметики. Рассматриваемые здесь примеры неумолимо наталкивают на мысль о целесообразности использования формата с плавающей точкой в том виде, как его трактует стандарт IEEE754.

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

Будем рассматривать два основных формата для ЧПТ — float и double. Напомним, что формат float позволяет представлять до 7 верных десятичных цифр в двоичной мантиссе, содержащей 24 разряда. Формат double представляетдо 15 верных десятичных цифр в мантиссе, содержащей 53 двоичных разряда.

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

СУММА


Итак, сначала рассмотрим сумму двух следующих действительных чисел, представленных в формате float, каждое из которых имеет по 7 верных значащих цифр:

0,6000006 + 0,03339874=0,6333993 4?0,6333993

Все вычисления будем проводить в формате float. Для наглядности будем использовать числа в распакованном виде. Представим наши десятичные числа в нормализованном двоичном виде:

0.6000006 ? 1.001100110011001101001?2?^(-1)
0.03339874?1.00010001100110100011110?2?^(-5)

Если полученные двоичные коды наших чисел опять представить в десятичном виде, то получим следующие значения:

0.6000006 ? 0.6000006 198883056640625?0.6000006 2
0.03339874?0.03339873 9993572235107421875?0.0333987 4

Здесь каждое число, округленное до 7 верных цифр мы отделили от «хвоста» пробелом. Эти «хвосты» получились в результате обратной конвертации чисел из двоичного кода, записанного в машинной мантиссе, в десятичный код.
Сумма наших чисел в двоичном 24-х разрядном виде даст следующий результат:

1.001100110011001101001?2?^(-1) + 1.00010001100110100011110?2?^(-5)? 1.0100010001001100111011?2?^(-1) ?0,6333994

Тот же результат будет получен, если просуммировать числа с «хвостами»:

0,6000006 2+ 0,0333987 4= 0,6333993 6? 0,6333994

Как мы видим, после округления до 7 значащих цифр, здесь получен результат, отличный от того, который получился при суммировании десятичных чисел на калькуляторе. Правила округления двоичного представления чисел, заложенные в стандарте IEEE754, не решают проблему точного вычисления, рассмотренных здесь чисел. В нашем случае причина ошибки кроется в сочетании цифр, стоящих после последних верных цифр десятичных слагаемых, о которых, априори, ничего не известно.

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

6543.455+12.34548=6555.80048?6555.800

Приведем наши десятичные слагаемые к двоичному нормализованному виду:

6543.455=1.10011000111101110100100?2?^12=6543.455 078
12.3454810 = 1.10001011000011100010110?2?^3 ? 12.345 48

Сумма этих слагаемых в двоичном виде даст следующее двоичное число:

1.10011001101111001101?2?^12?6555.80078125?6555.801

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

УМНОЖЕНИЕ


Такая же проблема неточных вычислений возникает при нахождении произведения некоторых ЧПТ, представленных в двоичном коде в формате float. Для примера рассмотрим произведение следующих действительных чисел:

0.06543455*139=9.095402 45?9.095402

Представим в этом выражении сомножители в нормализованном двоичном виде:

0.06543455=1.00001100000001010001101?2?^(-4)
139=1.0001011?2?^10

Результатом перемножения наших чисел в двоичном виде будет число:

1.00001100000001010001101?2?^(-4) ? 1.0001011?2?^10 = 1001.00011000011011000101 ?9.095403

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

ДЕЛЕНИЕ


Аналогично умножению, операция деления в формате float для некоторых ЧПТ также приводит к фатальным ошибкам. Рассмотрим следующий пример:

131/0.066?1984.848

Представим делимое и делитель в двоичном формате, в нормализованном виде:

13110 = 1.0000011?2?^7
0.066= 1.00001110010101100000010?2?^(-4)

Частное от деления наших чисел будет следующим:

1.0000011?2?^7/1.00001110010101100000010?2?^(-4)=
= 1.11110000001101100100111?2?^10 = 1984.848 5107421875?1984.849

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

ВЫЧИТАНИЕ


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

Пусть уменьшаемое у нас будет равно 105.3256. Вычтем из него число 105.32. Разность этих чисел, вычисленная вручную, будет равна:

105.3256-105.32=0.0056

Представим десятичное уменьшаемое и десятичное вычитаемое в нормализованном двоичном виде:

105.3256 = 1.10100101010011010110101?2?^6?105.3255 997 041015625
105.32= 1.10100101010001111010111?2?^6?105.32

Найдем разность этих чисел в двоичном виде:

1.10100101010011010110101?2?^6-1.10100101010001111010111?2?^6= 1.01101111?2?^(-8)

После преобразования этого числа в десятичный вид получим:

1.01101111?•2?^(-8)= 0.005599976

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

ОШИБКИ В ФОРМАТЕ ДАБЛ

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

В качестве инструмента для проверки правильности наших рассуждений будем использовать Excel 2009, в котором реализованы вычисления в строгом соответствии со спецификацией стандарта IEEE754.

Проведем следующие вычисления, используя средства Excel 2009. Формат ячеек выберем числовой, с 18 знаками после запятой. Для нахождения суммы запишем в ячейки таблицы Excel следующие числа:

A1= 0,6236
A2= 0,00661666666070646

В ячейке А3Excel получим сумму этих чисел:

А3=А1+А2=0,6236+0,00661666666070646?0,630216666660707

Если посчитать эту сумму вручную или на калькуляторе, то получится число:

0,6236+0,00661666666070646?0,630216666660706

Которое в младшем разряде не совпадает с тем, что получено в Excel.

Посмотрим, к чему приводит операция вычитания в Excel. Запишем в ячейки следующие числа:

А1= 123456,789012345
А2= 123456

В ячейке А3 найдем разность этих чисел. Она будет равна:

А3=А1-А2=0,789012345005176

А мы ожидали получить число:

123456,789012345-123456=0, 789012345

Выводы делайте сами!

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

А1= 0,500000000660006
А2 = 0,0000213456548763
А3 = 0,00002334565487363
А4 = 0,000013345654873263

В ячейке А6 запишем формулу =A1/5+A2. После чего в ней будет получен результат.

A6= A1/5+A2= 0,100021345786878

В ячейке A7 произведем следующие вычисления:

A7=A6/3+A3=0,0333637942504995

А теперь вычислим

A8=A7/4+A4=0,00835429421749813

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

A1/5=0,500000000660006/5=0,100000000132001 2?0,100000000132001
A1/5+A2=0,100000000132001+0,0000213456548763? 0,100021345786877
( A1/5+A2)/3=0,100021345786877/3?0,0333404485956257
( A1/5+A2)/3+A3=0,0333404485956257+0,00002334565487363? 0,0333637942504993
[( A1/5+A2)/3+A3]/4=0,0333637942504993/4?0,00834094856262483
[( A1/5+A2)/3+A3]/4+A4=0,00834094856262483+0,000013345654873263=0,00835429421749809

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

Комментарии (197)


  1. Guyverjoke
    13.09.2016 09:40
    +2

    Думаю, стоит убрать капс из заголовка поста.


    1. Innotor
      13.09.2016 09:40

      Убрал


  1. 4dmonster
    13.09.2016 09:43
    +7

    Так это всё общеизвестные факты.

    Всё равно что написать статью «НЕВОЗМОЖНОСТЬ ПРЕДСТАВЛЕНИЯ ЦЕЛЫХ ОТРИЦАТЕЛЬНЫХ ЧИСЕЛ С ПОМОЩЬЮ ТЕКУЩЕЙ РЕАЛИЗАЦИИ ДВОИЧНОЙ СИСТЕМЫ СЧИСЛЕНИЯ В ЭЛЕКТРОННЫХ ВЫЧИСЛИТЕЛЬНЫХ МАШИНАХ.»


    1. Innotor
      13.09.2016 09:48
      +1

      В статье речь идет о результатах арифметических вычислений действительных десятичных чисел с использованием двоичной арифметики.


      1. 4dmonster
        13.09.2016 10:21

        Я просто по аналогии привёл пример.
        В учебниках же пишут про float и double, что они не точные.

        Ну а в качестве иллюстрации нагляднее такое поведение — просто преобразование одного и того же числа 0.18 в VBA:
        из double в float

        ?csng(cdbl(0.18))
         0.18 
        

        из float в double
        ?cdbl(csng(0.18))
         0.180000007152557 
        


        1. Innotor
          13.09.2016 11:25
          -1

          В учебниках же пишут про float и double, что они не точные.

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


          1. 4dmonster
            13.09.2016 11:28

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


            1. Innotor
              13.09.2016 11:39
              -1

              К сожалению я не нашел этого очень старого учебника и поэтому ничего возразить не могу.


              1. Danov
                13.09.2016 23:10

                Ребятам даю пример:
                0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1 != 1.0
                и далее объясняю нюансы.

                Сам пример даю так:

                a = 0;
                while (a != 1) a += 0.1; 
                

                Какое значение будет после окончания цикла? Или сколько итераций цикла будет выполнено?

                Шок гарантируется. Потому хорошо запоминается, что вещественные числа не стоит проверять на равенство и неравенство.


    1. Innotor
      13.09.2016 10:16

      Дайте ссылку, где эта проблема рассматривается в таком контексте.


  1. rafuck
    13.09.2016 10:18
    +9

    1) Все ваши примеры эксплуатируют один и тот же эффект: не каждая конечная десятичная дробь является конечной дробью в двоичной системе счисления. Тут как бы на IEEE754 не стоит всех собак вешать. Скорее, следует сетовать на конечность представления чисел (каким бы оно не было).
    2) Получить при вычислениях непредсказуемые результаты, конечно можно, если вы используете неустойчивые алгоритмы.


    1. Innotor
      13.09.2016 11:11

      " Все ваши примеры эксплуатируют один и тот же эффект: не каждая конечная десятичная дробь является конечной дробью в двоичной системе счисления. Тут как бы на IEEE754 не стоит всех собак вешать. Скорее, следует сетовать на конечность представления чисел (каким бы оно не было)".


      Вы совершенно правы. И про IEEE754 вы правы. Стандарт работает с теми числами, которые ему предлагают. Но как Excel понять, где правильное число, а где не правильное. Если в большинстве случаев на выходе правильный результат, а в некоторых, случайных, не правильный, как быть?
      2) Получить при вычислениях непредсказуемые результаты, конечно можно, если вы используете неустойчивые алгоритмы.

      Но почему же Excel использует неустойчивый алгоритм стандарта IEEE754 для сложения, вычитания и проч. Если на выходе мы имеем непредсказуемый результат?


      1. 4dmonster
        13.09.2016 11:18

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


        1. Innotor
          13.09.2016 11:35

          Если Вас устраивает экономия памяти, при которой вы получаете результат с относительной погрешностью 0.6% на одной операции, то конечно проблем не будет.
          0,0056-0,005599976 =0,005599976/105.3256 ?0,006*100=0.6%


          1. 4dmonster
            13.09.2016 11:40

            Если нужна точность — используют или фиксированную точку или готовые библиотеки или пишут что-то самодельное.


          1. MiXei4
            14.09.2016 04:09

            А можно подробнее откуда такое число 0.6% относительной погрешности?
            (0,0056 — 0,005599976) / 0,0056 ? 0.000004 относительной погрешности. Нет?


            1. Innotor
              14.09.2016 07:46

              Да, вы правы, в пылу полемики неправильно посчитал.


      1. rafuck
        13.09.2016 12:55
        +1

        Вот смотрите, простой пример:

            float  af = 0.1;
            double ad = 0.1;
        
            printf("af - ad = %le;\n", af - ad);
        

        Ответ:
        af - ad = 1.490116e-09;
        

        Когда я написал
        float a = 0.1
        

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

        К слову, вот этот алгоритм во float неустойчив:
            const int N = 1e8;
            float af = 0.1;
            float S = 0;
        
            for(int i=0; i<N; ++i){
                S += af;
            }
        
            printf("error = %le\n", 0.1*N - S);
        

        Ответ:
        error = 7.902848e+06
        


        1. Innotor
          13.09.2016 15:08

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

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


          1. rafuck
            13.09.2016 18:36

            Какой именно пример из приведенных вами доставляет такие страдания? Я просто не понимаю, что вы имеете в виду под «младшим разрядом» и как он соотносится с порядком самих чисел.

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


            1. Innotor
              13.09.2016 20:46

              Какой именно пример из приведенных вами доставляет такие страдания?

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

              А вы уверены, что такой неожиданный результат из придуманных нами, случайно не выскочит при контроле, например, за ядерной установкой?


              1. rafuck
                14.09.2016 00:43

                В последнем примере вы сравниваете расчеты в Excel с вычислениями на калькуляторе, при этом каждое действие на калькуляторе округляете до 15 знака после запятой. Так?
                В итоге результат Excel и калькулятор совпадают в 15 знаках после запятой. Я все правильно понимаю?
                Простите, может я слишком хочу спать и неправильно интерпретирую ваши 18 значные дроби.


                1. Innotor
                  14.09.2016 07:16

                  Правильно. Если вычислять по правилам арифметики с точностью до 15 знака, с калькулятором мы делаем все верно. Если вычислять до 15 знака после запятой в формате дабл, и значащие цифры считать от точки в ненормализованном числе, то хвост не мешает. Арифметические операции не приближают неверные цифры к точке. Но, если считать по количеству значащих десятичных цифр, или от точки, в нормализованном числе, то хвост попадает в область верных цифр или участвует в образовании младшей значащей десятичной цифры. Проблема в том, что отсечение хвоста в двоичном коде увеличивает хвост в десятичном представлении и приближает неверные десятичные цифры к точке. Чтобы все было корректно, надо на каждом операционном шаге десятичное представления результата округлять до нужного значения


                  1. rafuck
                    14.09.2016 09:04

                    Вот честно, два раза перечитал ваш комментарий и ничего не понял. Вы сравниваете два результата вычислений. Один провели сами с точностью до 10^(-15), другой доверили экселю. Получили совпадение в 15 знаках после запятой. И вам почему-то это не нравится. А вот я бы, наверное, не доверял скорее результату ваших вычислений с арифметическими округлениями, а не округлением до четного.


                    1. Innotor
                      14.09.2016 10:19

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


                      1. rafuck
                        14.09.2016 10:31

                        1.25/0.25 = 5
                        теперь, следуя вашей логике, я округляю до десятых, надеясь получить один правильный знак после запятой (N = 1)
                        1.3/0.3 = 4.(3)
                        Сколько верных знаков после запятой у меня получилось?


                        1. Innotor
                          14.09.2016 10:51

                          1. rafuck
                            14.09.2016 10:53

                            Я что-то неправильно округлил?


                        1. Innotor
                          14.09.2016 11:26

                          Да, по правилам арифметики так и получается. Как говорится «что выросло, то выросло»


                          1. rafuck
                            14.09.2016 11:37

                            Я цитирую вас:

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

                            я вам показал только что, что это не так.


                      1. vadimr
                        14.09.2016 11:16

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


                        1. Innotor
                          14.09.2016 11:38

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


                          1. vadimr
                            14.09.2016 11:43

                            Зачем вам в жизни вещественные числа? В типичном случае, компьютер получает информацию от датчиков в двоичной форме, обрабатывает её в двоичной форме и выдаёт на исполнительные механизмы тоже в двоичной форме. А выдача человеку на экран носит чисто справочный характер.

                            В Вашем примере с ядерной установкой, десятичной системе просто неоткуда взяться.


                            1. Innotor
                              14.09.2016 12:32

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


                          1. DistortNeo
                            14.09.2016 13:08

                            Если бы мы использовали 16-ричную систему, то проблемы бы остались такими же.


                            1. vadimr
                              14.09.2016 18:36

                              Ну, проблемы неточности представления двоичных дробей не было бы.


                              1. DistortNeo
                                14.09.2016 19:45

                                Почему? Все равно округлять бы пришлось из-за ограничения на длину мантиссы.


                                1. vadimr
                                  14.09.2016 20:13

                                  Если бы в длину мантиссы по количеству знаков не уложились, то да. Но короткие шестнадцатиричные дроби кодируются точно, в отличие от некоторых десятичных, вроде 0.2.

                                  Часть проблем автора поста связаны с тем, что не все конечные десятичные дроби имеют конечное двоичное представление.


                                  1. Innotor
                                    14.09.2016 20:40

                                    16-ричные дробные числа, не переполняющие разрядную сетку мантиссы конечно могут быть точно представлены в двоичном коде. Это две соизмеримые системы счисления. Но, когда приходится делить два таких числа друг на друга мы опять можем получить бесконечную дробь:
                                    0.9 ? 0.7 = 1.49(249) в 16-ричной системе счисления.


                                  1. DistortNeo
                                    14.09.2016 20:40

                                    Смотря что эти 0.2 означают. Если это именно точное значение, то логичнее его представлять как 1/5, а не 0.2.

                                    Если же это результат физического измерения (масса, например), то точность измерения будет на порядки ниже точности округления. Если значение измерено с точностью до 1 знака, то мы можем смело 0.2 представить как 21/256, и ничего не потеряем.


          1. Refridgerator
            14.09.2016 10:34

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

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


            1. Innotor
              14.09.2016 10:56

              Алгоритм Кэхэна работает за счет привлечения еще одного операционного программного регистра в дополнение к аппаратному. Это все равно, что увеличить в два раза аппаратный регистр. При этом проблема неверных цифр («хвоста») не пропадает.


              1. Refridgerator
                14.09.2016 11:08

                Это все равно, что увеличить в два раза аппаратный регистр.

                Это как? По моей логике, «увеличить в два раза аппаратный регистр» = «увеличить точность в 2 раза», в то время как в алгоритме Кэхэна ничего подобного не наблюдается. Там просто контролируется накопление погрешности. Опять же, с ограниченной точностью.


                1. Innotor
                  14.09.2016 11:28

                  А алгоритм Кэхэна для того и придуман, чтобы увеличить точность вычислений в 2 раза.


                  1. rafuck
                    14.09.2016 11:41

                    Ничего подобного, абсолютная точность алгоритма совпадает с точностью переменной, в которой копится сумма. Относительная погрешность лучше — это да.


                  1. Refridgerator
                    14.09.2016 11:41

                    А вы можете обосновать, что точность увеличивается именно в 2 раза, а не, скажем, в полтора? Откуда вообще взялась эта цифра? Ни в русской, ни в английской википедии её нет.


                    1. Innotor
                      14.09.2016 12:43

                      Конечно же не в 2 раза, а в 10^N для десятичных чисел или в 2^N для двоичных чисел. Где N — количество разрядов, выделенное для учета дробных значений, которые вышли за пределы разрядной сетки операционной мантиссы. Учет этих значений уменьшает абсолютную погрешность и как следствие относительную.


                      1. Refridgerator
                        14.09.2016 13:06

                        Выглядит ещё менее убедительно. Если честно, выглядит так, как будто вы эту формулу взяли просто из головы. Как минимум потому, что в ней отсутствует количество суммируемых элементов. Кроме того из неё следует, что если для хранения накапливаемой погрешности вместо double взять long double, это приведёт к экспоненциальному росту точности. Что сомнительно. Ведь вычисления производятся по-прежнему в оригинальном формате.


                        1. Innotor
                          14.09.2016 13:37

                          В примере из Википедии для старших цифр десятичных чисел отведено 6 разрядов и столько же для погрешности. В общей сложности получается, что число представляется 6+6+12 разрядами. Каждый новый разряд увеличивает точность представления числа в 10 раз для десятичных чисел. Вот и получается, что за счет дополнительного программного регистра точность повысилась в 10^6 раз.


                          1. Innotor
                            14.09.2016 20:55

                            6+6=12


        1. DistortNeo
          13.09.2016 16:51

          А вы не используйте алгоритмы, которые допускают такое накопление.
          Например, для обучения свёрточных нейронных сетей зачастую достаточно даже не float, а half-float (2 байта).


          1. rafuck
            13.09.2016 18:26

            А я и не использую ,)



  1. Andy_U
    13.09.2016 11:12

    Десятичные числа (с ограниченным числом разрядов) — рациональные(т.е. лишь счетное подмножество вещественных числе), а не вещественные. Как, кстати, и двоичные.


    1. Innotor
      13.09.2016 11:14

      Именно этот факт и приводит к тем ошибкам, о которых в статье говорится.


      1. Andy_U
        13.09.2016 13:16
        +2

        Я лишь хотел заметить, что нет такого математического понятия «десятичные действительные числа», которое Вы использовали не только в комментарии, на который я написал ответ, но, и, если внимательно посмотреть, то и в статье. Возможно это и есть причина, по которой многие ваши утверждения математически некорректны. Например, Вы пишете в разделе про умножение:

        Представим в этом выражении сомножители в нормализованном двоичном виде:

        0.06543455=1.00001100000001010001101?2?^(-4)
        139=1.0001011?2?^10

        Так вот, число 0.06543455 лишь приближенно равно 1.00001100000001010001101?2?^(-4), (точное двоичное значение — двоичная периодическая дробь, т.е. число с бесконечным количеством знаков), что собственно говоря и является причиной, почему 139*0.06543455 !=139*1.00001100000001010001101?2?^(-4)


        1. Innotor
          13.09.2016 15:24

          Так в чем неточность? В термине «действительное число»? Да, я с вами согласен, это число рациональное. Почему-то в литературе, в том числе в стандарте IEEE754, дробные рациональные числа называются действительными. Я не стал менять эту традицию, т.к. для данного случая это не принципиально.


          1. Andy_U
            13.09.2016 16:39
            +4

            Хорошо. Но теперь Вам стало понятно, что равенство (из Вашей статьи):

            0.06543455=1.00001100000001010001101?2?^(-4)

            лишь приближенное? Ведь слева и справа рациональные числа, т.е. дроби, где слева знаменатель 10^8, а справа 2^27, а 5 на два не делится. Такие дроби никак не могут быть в общем случае равными.

            Теперь заменяем везде в статье, где слева десятичная дробь, а справа двоичная, знак точного равенства на знак приближенного равенства. И видим теперь, что Ваш текст «разошелся» с математикой. Попробуйте его скорректировать чтобы и математика не пострадала, и идея вашей статьи. Я сомневаюсь, что это возможно.

            Далее, почему «виноватой» в во всем этом «безобразии» оказывается именно двоичная система счисления? А если бы в компьютере была использована троичная система счисления? Или пользователь бы троичную систему счисления использовал, а компьютер десятичную? Где потерялась симметрия?

            Ну и наконец, во всех физических и математических расчетах рациональных чисел не бывает. Чему там равна скорость света? А никто точно не знает. Лишь доверительный интервал известен (между двумя рациональными числами). Плевать на ошибку оцифровки в значительном числе случаев.


            1. Innotor
              13.09.2016 18:07
              -2

              Ведь слева и справа рациональные числа, т.е. дроби, где слева знаменатель 10^8, а справа 2^27, а 5 на два не делится. Такие дроби никак не могут быть в общем случае равными.

              Вы правы, в приведенном равенстве надо поставить знак приближения. Число справа, если его привести к десятичному виду не будет равно десятичному числу слева. Вот эта разница в представлении и дает ошибку в вычислениях.
              И видим теперь, что Ваш текст «разошелся» с математикой.

              Так где расхождение?
              Далее, почему «виноватой» в во всем этом «безобразии» оказывается именно двоичная система счисления?

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


              1. Andy_U
                13.09.2016 22:34

                Так где расхождение?

                Так вы же сами совершенно правильно ответили:

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

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


                1. Innotor
                  13.09.2016 23:23

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


                  1. Andy_U
                    13.09.2016 23:40

                    Это не ошибки, а погрешности. Т.е. не bug, а feature. И Вы правильно сейчас написали, что причина в невозможности точно преобразовать числа конечной длины из N-ричной системы счисления в M-ричную. Т.е. пока мы используем лишь одну систему счисления, проблемы нет. Но Ваша статья называется «Фатальные ошибки двоичной арифметики...».


                    1. Innotor
                      13.09.2016 23:54
                      -1

                      Вы напрасно поставили многоточие. Речь идет именно об ошибках (или погрешностях) при двоичных вычислениях с десятичными ЧПТ. Не буду спорить по поводу семантики понятий bug и feature, но на мой взгляд последствия очевидны, как в том, так и в другом случае.


                      1. Andy_U
                        14.09.2016 00:23
                        +1

                        Вы напрасно поставили многоточие.

                        Не напрасно. В заголовке Вашей статьи (Фатальные ошибки двоичной арифметики при работе с числами с плавающей точкой) нет упоминаний про десятичную систему счисления.

                        Речь идет именно об ошибках (или погрешностях) при двоичных вычислениях с десятичными ЧПТ.

                        Ну нет внутри современных компьютеров (ну уровне железа) десятичных чисел с плавающей запятой. Только двоичные.


                        1. Innotor
                          14.09.2016 06:58

                          Да, я с Вами согласен. Надо было уточнить про десятичные числа. Боюсь уже поздно.


            1. olgerdovich
              13.09.2016 20:14

              анекдот в том, что как раз скорость света (в м/с ) является целым числом (точно 299792458), через которое определяется метр.


              1. Andy_U
                13.09.2016 23:43

                Ваша правда. Неудачный я пример привел.


  1. Siemargl
    13.09.2016 11:12
    +1

    Дело не только в формате представления чисел.

    Во первых, FPU выполняет вычисления с внутренней точностью, далеко превосходящей даже long double (10-байт). Усечение происходит «на выходе»

    Во вторых, на самом деле FPU считает «не совсем по стандарту». В тоже же библиотеке, например GCC, масса воркэраундов. AFAIK -ffast-math будет выполнять расчеты только инструкциями FPU, и результат может вас удивить.


    1. Innotor
      13.09.2016 11:45

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


    1. Siemargl
      13.09.2016 13:40

      Во первых, FPU выполняет вычисления с внутренней точностью, далеко превосходящей даже long double (10-байт). Усечение происходит «на выходе».
      Да похоже я слегка загнул — внутренние вычисления происходят в x87 в 80-битном виде. Т.е операнды расширяются к 80-бит, выполняются вычисления и после этого происходит обратная конверсия с понижением точности.

      Если же используются SSE/AVX то там максимум 64-бит числа с плавающей точкой.

      Утверждается (например на stackoverflow), что с предварительным расширением до 80-бит и без него можно получить разные результаты вычислений.


      1. Innotor
        13.09.2016 15:30

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


  1. boolivar
    13.09.2016 11:22

    x87 все вычисления производит в 80-битном расширенном формате, и для double и для float


    1. Innotor
      13.09.2016 11:48

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


      1. napa3um
        13.09.2016 12:27

        В общем случае «хвост» в FPU уже есть, просто на выходе мы получаем меньшую точность этого хвоста. Математики, которым критичен этот хвост, и так в курсе того, что компьютер может хранить лишь конечное множество всех возможных значений, в курсе мантисс и экспонент, и в курсе различий между численным и аналитическим решением уравнения. Выводы делайте сами!


        1. Innotor
          13.09.2016 12:45

          Ну, про точность хвоста, мы ничего не можем сказать, нам про него ничего не известно.

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


          1. napa3um
            13.09.2016 12:49
            +2

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


            1. Innotor
              13.09.2016 13:12

              Ваш юмор я оценил. Но как быть с расчетами?


              1. napa3um
                13.09.2016 13:47

                Считать, учитывать.


                1. Innotor
                  13.09.2016 14:20

                  А как учитывать, если из заявленной точности — 15 достоверных цифр после запятой для нормализованных чисел формата double, верными оказываются только 7?


                  1. napa3um
                    13.09.2016 14:23

                    Озвучьте вашу версию ответа, пожалуйста.


                    1. Innotor
                      13.09.2016 14:57

                      Думаю, ответ такой. Не использовать в расчетах с действительными (рациональными) числами двоичную арифметику в том виде, как она реализована в стандарте. Причем не важно, как она реализована, на аппаратном или на программном уровне. Вся беда в том, что проблемы начинаются, когда десятичное число содержит дробную часть. Целые числа не приводят к ошибкам представления. Поэтому, если представлять числа в виде ЧПТ с целочисленной мантиссой, эти проблемы не возникают. Возникают другие проблемы, но они не системные и преодолимы. Попытка ответить на ваш вопрос была предпринята в моем топике https://habrahabr.ru/post/272251/. В комментариях можно найти ссылку на PDF-файл.


                      1. napa3um
                        13.09.2016 15:35

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


                        1. Innotor
                          13.09.2016 16:09

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


                          1. napa3um
                            13.09.2016 16:13

                            Так же, как «были» до этого.


          1. Tujh
            13.09.2016 12:53
            +3

            А Вы не используйте Excel для расчётов ядерного реактора и всё будет хорошо :)
            Честно-честно.


            1. Innotor
              13.09.2016 13:15

              В статье речь идет ни об ошибках в Excel, а проблемах работы с ЧПТ, которые представлены в двоичном коде. Excel был взят для иллюстрации.


          1. Innotor
            13.09.2016 13:10

            Вот программка, которую по моей просьбе написал Дмитрий Пикулик.
            /*
            * gcc -O0 -Wall -msoft-float -o test test.c
            */
            #include <stdio.h>
            #include <assert.h>
            #include <float.h>
            #include <math.h>

            static double f(double a, double b)
            {
            double r = a — b;

            return r;
            }

            int main (int argc, char **argv)
            {
            double a = 0.66;
            double b = 0.659999996423721;

            x = f(a, b);

            printf(«x=%.40f\n», x);

            return 0;
            }

            x=0.00000000357627905067658 95770513452589512 ?0.00000000357627905067659

            Сравните с тем, что получится на калькуляторе.


            1. Tujh
              13.09.2016 13:28
              +2

              Задайте себе вопрос, почему, при математических расчётах ЧПТ на ПК сравнение выполняют не так:

              double a = f1(), b = f2();
              if ( a != b ) {… }
              а так:
              double a = f1(), b = f2();
              if ( fabs( a — b ) > EPSILON ) {… }
              и на сколько полезна ваша серия статей (аж в год длинной) про недостатки текущего стандарта?
              Всё известно, кто хочет точности — пользуется специальными библиотеками.


              1. Innotor
                13.09.2016 14:16
                -1

                Разница между двумя числами double a = 0.66 и double b = 0.659999996423721 составляет 0,000000003576279. Вопрос, какое должно быть EPSILON, чтобы убедиться, что это число равно числу 0.00000000357627905067659

                на сколько полезна ваша серия статей (аж в год длинной) про недостатки текущего стандарта?


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


                1. Tujh
                  13.09.2016 15:24
                  +1

                  Вопрос, какое должно быть EPSILON, чтобы убедиться, что это число равно числу 0.00000000357627905067659

                  cfloat / float.h
                  FLT_EPSILON 1E-5 or smaller
                  DBL_EPSILON 1E-9 or smaller
                  LDBL_EPSILON 1E-9 or smaller

                  EPSILON — Difference between 1 and the least value greater than 1 that is representable.

                  То есть даже для float это «всего лишь» 0,00001


                  1. Innotor
                    13.09.2016 15:56

                    По-моему кавычки в словах «всего лишь» надо убрать, т.к. 24 двоичных разряда мантиссы формата float обеспечивают представление десятичного числа с точностью до 7 знаков после запятой.


                    1. Tujh
                      13.09.2016 16:01

                      Кавычки тут больше для привлечения внимания, допустимая точность для float не 7, а пять знаков после запятой, именно это и отражает постоянная FLT_EPSILON


                      1. Innotor
                        13.09.2016 16:29

                        Если мы говорим о представлении десятичного числа в двоичном коде, то 24 двоичных разряда мантиссы нормализованного числа позволяют представить любое десятичное число с 7 верными цифрами. Почему же вы ограничились 5 знаками?


                        1. Tujh
                          14.09.2016 08:17

                          Если грубо — 5 знаков это допустимая точность вычислений для float, 9 — double. Любые вычисления, требующие большей точности требуют смены типа переменных. Например, если мы считаем некоторую величину где будут учитываться 5 и 6 знаки после запятой — float уже не подходит, его точности не хватает, выбираем double. Если нужна точность более 9 знаков — дабл так же не подходит. Это основы программирования с ЧПТ, учат ещё когда проходят эти числа.
                          Вы сейчас боретесь или с ветряными мельницами.


                          1. Innotor
                            14.09.2016 09:31

                            Посчитайте в формате float, чему это равно: 1234,567-1234,56=?
                            У меня получилось 0.0069580


                            1. Tujh
                              14.09.2016 10:00

                              #include <cmath>
                              #include <cfloat>
                              #include <iostream>
                              
                              int main()
                              {
                                  float a = 1234.567, b = 1234.56;
                                  float c = a - b;
                                  float real_c = 0.007;
                                  std::cout << "1234.567 - 1234.56 = " << c << std::endl;          // 1234.567 - 1234.56 = 0.00695801
                              
                                  std::cout << "c - \"real c\" = " << ( real_c - c ) << std::endl; // c - "real c" = 4.19924e-05
                              
                                  return 0;
                              }

                              при замене на double:
                              // 1234.567 - 1234.56 = 0.007
                              // c - "real c" = -6.18455e-14

                              Так и не понял что Вы хотите этим доказать?
                              О «точности» float все знают.


                              1. rafuck
                                14.09.2016 10:14

                                Да это уже на троллинг со стороны топикстартера похоже, если честно. Когда вы записали 1234.567 во float, вы уже ошиблись в 5 десятичном знаке после запятой. То же самое с 1234.56.


                                1. Innotor
                                  14.09.2016 10:41

                                  Так и я о том же. Как только вы преобразовали десятичное число к двоичному, вы ошиблись не в 5 знаке, а в 10000… знаке. Так как получили бесконечную дробь.


                                  1. rafuck
                                    14.09.2016 10:42

                                    В пятом десятичном знаке.


                                    1. Innotor
                                      14.09.2016 11:16

                                      В самом большом числе из приведенных всего 3 знака после запятой (1234.567). А вот после конвертации оно становится
                                      1234.56710 = 1.0011010010.100100010010011011101001?•2?^10
                                      =1234.5670166015625
                                      Так про какой 5 знак идет речь?


                                      1. rafuck
                                        14.09.2016 11:29

                                        Когда вы записали 1234.567 во float, вы уже ошиблись в 5 десятичном знаке после запятой.


                                        1. rafuck
                                          14.09.2016 11:33

                                          Остальные я не считал: 7->(4->8->6->2->4). И это неважно. При ошибке в пятом десятичном знаке после запятой мы имеем 8 правильных десятичных знаков в представлении исходного числа. Большего мы от float и не ждем.


                                          1. Innotor
                                            14.09.2016 11:45

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


                            1. rafuck
                              14.09.2016 10:07

                              1) И какова относительная погрешность?
                              2) Вы снова передергиваете и приводите числа, не представимые в конечном двоичном коде.
                              3) Какова относительная погрешность для чисел, которые вы в реальности записали в память?


                              1. Innotor
                                14.09.2016 10:45

                                1) И какова относительная погрешность?

                                Если говорить о погрешности представления — разная для разных чисел
                                2) Вы снова передергиваете и приводите числа, не представимые в конечном двоичном коде.

                                Я все время именно о таких числах и веду речь. Представимые числа — точные и с ними проблем нет до тех пор пока в результате арифметических действий они не превращаются в приблизительные.


                                1. rafuck
                                  14.09.2016 10:51

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


              1. rafuck
                13.09.2016 18:46
                +1

                Не совсем так. Скорее, так:

                |a-b| < epsilon*max(|a|, |b|)
                


                1. Tujh
                  14.09.2016 08:21

                  Спасибо, не знал этого способа.


                  1. rafuck
                    14.09.2016 08:54

                    Смыл в том что расстояние между соседними машинными числами в формате IEEE754 зависит от их абсолютной величины. Например, следующее за 10^8 во float будет 10^8+8. Поэтому абсолютная точность, скажем, 0.1 при таких порядках будет в принципе недостижима.


                    1. Innotor
                      14.09.2016 09:41

                      Я не совсем понял. У нас рассматриваются все числа, у которых один десятичный порядок.


                      1. rafuck
                        14.09.2016 09:55

                        я уточнил, зачем нужно проверять на равенство считая относительную погрешность, а не абсолютную


                        1. Innotor
                          14.09.2016 10:29

                          Я про проверку на равенство советов не давал


                          1. rafuck
                            14.09.2016 10:43

                            Так исходный мой комментарий и не вам был адресован.


                            1. Innotor
                              14.09.2016 11:05

                              Извините, закопался в коментах.


    1. Refridgerator
      14.09.2016 09:42

      в FPU есть флаг точности с возможными режимами:

      00 – 24 бита мантисса, 7 бит порядок;
      10 – 53 бита мантисса, 10 бит порядок (двойная точность);
      11 – 64 бита мантисса, 15 бит порядок (расширенная точность).


      1. Innotor
        14.09.2016 09:42

        И что из этого следует?


        1. Refridgerator
          14.09.2016 10:08

          Это было уточнение комментария «x87 все вычисления производит в 80-битном расширенном формате, и для double и для float».

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

          Стек FPU ограничен восемью регистрами. Пока остаются свободные регистры для хранения промежуточных данных, очевидно, их точность остаётся неизменной. Но как только свободные регистры заканчиваются, компилятор вынужден сохранять их во внешней памяти — и обычно это делается в 64-х битном формате, с потерей 80-битной точности (как показало изучение ассемблерного кода от компиляторов Intel и Microsoft).


          1. boolivar
            14.09.2016 12:53

            Эти настройки точности больше для того, чтобы результаты не были точнее чем вы сами того ожидали, проверяя на вычислениях ограниченной точности (взято отсюда https://en.wikipedia.org/wiki/X87#Description):

            As this may sometimes be problematic for some semi-numerical calculations written to assume double precision for correct operation, to avoid such problems, the x87 can be configured via a special configuration/status register to automatically round to single or double precision after each operation


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


  1. vital72
    13.09.2016 14:22

    а как тогда считает калькулятор? или он не двоичный?


    1. Innotor
      13.09.2016 14:23

      Для точных вычислений существует двоично-десятичный формат, который реализует десятичную арифметику.


  1. vadimr
    13.09.2016 15:06

    Машинные операции с вещественными числами имеют ограниченную точность, что Вы и продемонстрировали на примерах.

    В эксплуатационной документации к первым ЭВМ так и писали, что машина получает не сам результат математической операции, а доступное приближение к нему. Потом как-то все привыкли, и писать перестали.


    1. Innotor
      13.09.2016 15:14
      -1

      Используя тот или иной инструмент, я должен знать, какую точность гарантирует этот инструмент. Если для double гарантирована точность до 15 десятичных знаков после запятой для нормализованных чисел, а по факту я имею всего 7, то как можно использовать этот инструмент?


      1. 4dmonster
        13.09.2016 15:22
        +1

        а где написано именно про гарантированную точность?


      1. vadimr
        13.09.2016 15:22

        В каком примере вы имеете 7?


        1. Innotor
          13.09.2016 15:50

          См. в статье, где приведен пример для вычитания чисел в формате double.


          1. Tujh
            13.09.2016 15:59
            +1

            0,789012345005176
            А мы ожидали получить число: 0, 789012345
            Погрешность меньше допустимой 1Е-9, так что всё укладывается в рамки допустимого.


          1. vadimr
            13.09.2016 16:24

            Там нет примера, только Ваши утверждения.

            #include <stdio.h>
            int main () {
            double d;
            d = 105.3256-105.32;
            printf («d = %lf\n», d);
            return 0;
            }

            d = 0.005600

            Если Эксель вам врёт, то причём тут машинная арифметика? Он вообще с символьными данными работает.


            1. Innotor
              13.09.2016 16:51

              Посмотрите в комментариях чуть выше. Там приведен пример для формата double.


              1. vadimr
                13.09.2016 16:55

                Вы можете конкретно написать пример? Из Ваших реплик ничего не понятно.


                1. Innotor
                  13.09.2016 17:03

                  Не могу, хабр. ограничивает ресурсы. Пишите в личку.


                  1. vadimr
                    13.09.2016 17:06

                    Если Вы имеете в виду свой комментарий “Вот программка, которую по моей просьбе написал Дмитрий Пикулик”, то в этой программе:

                    1) отсутствует определение переменной x, поэтому она не может быть скомпилирована;

                    2) после вставки определения “double x;”, результат, выдаваемый программой, соответствует теоретическому с точностью до 16 десятичных цифр, что отвечает точности формата double.


                    1. Innotor
                      13.09.2016 18:12

                      Посчитайте разность: 123456,789012345-123456.


                      1. vadimr
                        13.09.2016 18:42

                        0.789012345005176, разница в 18-м десятичном знаке.


                        1. Innotor
                          13.09.2016 19:40

                          123456,789012345-123456=0,789012345. Согласно вычислениям на калькуляторе виндоуз.


                          1. vadimr
                            14.09.2016 07:24

                            Ну берите какой-нибудь язык PL/I и тип decimal fixed, если вас интересует точное представление десятичных чисел. Как верно указано в соседней ветке, настоящие физические параметры не имеют точного представления ни в одной системе счисления, поэтому совершенно неважно, как их записывать. Главное, чтобы погрешность алгоритма соответствовала требованиям решаемой задачи.


                            1. Innotor
                              14.09.2016 09:15

                              Вопрос в цене-времени-сложности. Нет таких калькуляторов, которые работали бы на указанных вами принципах.


                              1. vadimr
                                14.09.2016 09:20

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


                              1. vadimr
                                14.09.2016 09:28

                                Причём, так как изначально практически все источники вещественных цифровых значений в современном мире имеют двоичную природу, то представление чисел в десятичной системе их только портит.

                                Рано или поздно человечество перейдёт к шестнадцатиричной в быту.


                                1. Innotor
                                  14.09.2016 12:05

                                  Ну, дай то Бог!


          1. vadimr
            13.09.2016 16:32

            a = 123456.789012345-123456.0;
            printf («a = %.15lf\n», a);

            a = 0.789012345005176

            Кто вам сказал, что Эксель работает с числами в формате double и в соответствии с IEEE 754?


            1. Innotor
              13.09.2016 17:00

              Microsoft


              1. vadimr
                13.09.2016 17:07
                +1

                К нему и предъявляйте претензии, а не к формату double. То, что в Экселе много залепух в арифметике, давно известно.


            1. Innotor
              17.09.2016 08:33

              Microsoft Excel was designed around the IEEE 754 specification to determine how it stores and calculates floating-point numbers.

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


              1. vadimr
                17.09.2016 09:10

                Тут не написано, что операции с числами в Экселе соответствуют стандарту IEEE 754. Написано, что Эксель спроектирован где-то вокруг этого стандарта (даже не знаю, как корректно перевести это бессмысленное маркетинговое утверждение).

                Я свои примеры даю на языке Си с использованием арифметики IEEE 754.


  1. LynXzp
    13.09.2016 16:27

    Курс дзена молодого бойца: попробуйте перемножить, а тем более уж поделить Точно одно-двух байтные целые числа на AVR.


    1. Innotor
      13.09.2016 17:06

      Мастер! Попробуйте с точностью до 6 знака после запятой найти частное 2/3 в двух разрядном калькуляторе.


      1. LynXzp
        13.09.2016 17:09

        Я никого не хотел обижать. Просто AVR может ошибаться уже в 4-х значных числах, и без запятой.


        1. boolivar
          13.09.2016 22:44

          Простите, но при чем тут AVR? Да, там нет сопроцессора, поэтому все операции с плавающей запятой производятся программно, это может сделать сам компилятор или использовать библиотечные реализации (http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_math).
          Однако, вы можете реализовать все операции самостоятельно, с любой точностью, какой пожелаете, ограничиваясь объемом памяти кристалла, конечно же.


          1. LynXzp
            13.09.2016 23:18

            На самом деле да, все как Вы сказали, я имел в виду скорее компилятор. Хотя не могу сказать по всем, но avr-gcc (самый популярный) и icc (очень редкий) генерируют неточный код. И у меня даже есть маленькая функция для быстрого и более точного деления на 10 (каждый раз при отображении чисел нужна).

            Но и это не все, там есть аппаратная функция FMUL которая умножает два байта. И она может ошибаться на 1-2 значения.

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


            1. boolivar
              14.09.2016 11:01

              Как можно ошибиться при умножении? Вы о чем вообще? Команда FMUL предназначена для операций с фиксированной запятой она сдвигает результат влево чтобы запятая результата оказалась на том же месте, что и у входных аргументов.
              Это значит, что если попытаться с её помощью перемножить два целых числа вы получите число в два раза большее чем ожидали.


              1. LynXzp
                15.09.2016 01:19
                +1

                Ныряю в пепел, стыд и позор. Про FMUL я просто слышал и не правильно понял, признаю (я С-шник). Но и в остальном я не могу воспроизвести ничего. Даже ошибку в делении. Заминусуйте начальные комментарии чтобы никто ересь не читал. С чего я это все взял — не знаю, может раньше так было, а может у меня повреждение памяти, жаль с ECC нельзя поставить.


        1. Innotor
          13.09.2016 23:15

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


          1. LynXzp
            13.09.2016 23:22

            Я не про переполнения конечно, иначе этот диалог, начатый мной, не имел бы смысла. Сходу не найду, но вот например есть калькулятор fmul calc — позволяющий смотреть где ошибается микроконтроллер avr при использовании этой инструкции (аппаратная инструция целочисленного умножения, принимает два байта, результат записывается в двухбайтовую переменную). Не знаю на сколько точен калькулятор, но эти ошибки имеют место быть.


  1. VaalKIA
    14.09.2016 02:46
    +2

    Что автор-то хочет, мне — не понятно…
    По моему, у него, каша в голове: Excel, какие-то листинги программы, обещания 7 знаков после запятой наследственной грамотой…
    Вы сами-то понимаете о чём речь?
    Если вы показываете листинг программы, где написанна десятичная константа, то она не десятичная, это всего лишь соглашение, точно так же как кодировка листинга (может быть в utf8, но это не значит, что print(«hello world») небудет в ANSI) и вывода программы. Если в листинге написано a := 0.6, почему вырешили, что в компьютере материализуется довоичнодесятичный аппаратный блок арифметики, а не разумное: «пишите как удобно, компилятор сам преобразует»?
    Значит вычёркивайте вообще все слова «десятичные» из текста.
    Почему вы утверждаете что при точности в 5 знаков результат сложения тоже 5 знаков? Теория говорит, что будет уже 4 точных знака и не надо делать выпученные глаза и тыкать нас в это носом, идите и читайте книжки.


    1. Innotor
      14.09.2016 07:40

      Уважаемый! Нельзя ночью писать коменты, Судя по всему, при изложении своих мыслей, вы пользуетесь принципом «пишите как удобно, компилятор сам преобразует».
      Если по существу,

      Почему вы утверждаете что при точности в 5 знаков результат сложения тоже 5 знаков?

      Если следовать вашим знаниям, то в формате float, после 7 шагов суммирования, мы уже имеем совершенно неверный результат, даже, если складывались точные числа. Ну или 16 шагов для дабл.


      1. VaalKIA
        14.09.2016 14:02

        Когда говорят о точости, делают оценку сверху. Так что ваш ответ зависит от того, как считать, вы же не будете спорить с тем, что мы можем только приближённо прогнозировать худший вариант, а что будет на самом деле надо уже смотреть.
        Если в кратце, то замените разряд на X и подумайте сами, способен ли он после сложения повлиять на старшие знаки?
        1.0X+1.1X= это может быть, например, так 1.09+1.19 =2.28 а не 2.1X, точность — упала. После 7 шагов она упадёт ещё больше, но оценка сверху по частям и общая оценка сверху будут разниться, так же как и в бухгалтерии сумма НДС товаров не равна НДС от суммы товаров.
        Проще говоря, если вам числа даны с определённой точностью, суммирование будет накапливать погршеность, умножение её преумножать, на каждой итерации всё больше разрядов могут отличаться и на целую часть это тоже распространяется. Есть попытки бороться с этим в финансах внося стохастическую составляющую. но по факту смысл не меняется и совсем от этого никуда не деться, можно лишь немного изменять масштабы бедствия в конкретных практических случаях.


    1. Siemargl
      14.09.2016 17:52

      Я бы в подобный случаях советовал использовать hexfloat (%a) формат, чтобы различить, где у вас исходная десятичная константа уже с округлением.

        cout << 0x0.123p-1 << endl;
        cout << std::hexfloat << 6.6;
      


  1. mmMike
    14.09.2016 06:37

    Может я не прав, но тем кто про эти очевидные вещи знает — статья не очень интересна.
    А тем, для кого это "открытие" лучше было бы еще привести практические рекомендации (и примеры придумать/привести) типа :


    1. Никогда не сравнивай результат вычисления double/float типа с константой на равенство.
    2. Ошибки вычисления double/float имеют свойство накапливаться и результат может быть очень непредсказуемым.
      3… и т.д.

    впрочем, хозяин барин...


    1. Innotor
      15.09.2016 08:20

      Ошибки вычисления double/float имеют свойство накапливаться и результат может быть очень непредсказуемым
      .
      Это, если использовать медицинские аналогии, которые здесь приводил один пользователь, все равно, что сказать: " Я вам даю таблетку от головной боли, но последствия непредсказуемы".


      1. mmMike
        15.09.2016 08:59
        +1

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


        А вычислениям с плавающей точкой посвящены целые главы литературы по программированию. Включая и кучу примеров где проблемы и методы их решения рассматриваются.


        Причем эта тема весьма стара и классическая. Начиная с FORTRAN 66, когда программированием начали заниматься люди, не вникающие (да не надо им это) как выполняется, например, работа с числами с плавающей точкой на разных архитектурах и пр.
        Да самое элементарное (одно из..) — выравнивание (умножение + деление результата на константу) там где нужно исходя из задачи и предполагаемой размерности чисел.


        Тут проскакивало что то про расчет на калькуляторе и на компе и что цифры не совпадают… А ничего что калькуляторы используют в основном двоично десятичный код для вычисления? Интересно, много людей, что здесь дискутирую вообще знают что это и почему используется?


        1. Innotor
          15.09.2016 09:51

          А вычислениям с плавающей точкой посвящены целые главы литературы по программированию. Включая и кучу примеров где проблемы и методы их решения рассматриваются.

          Ну да, как у Макаренко в «Республике ШКИД» — «Теоретически она лошадь, а практически она падает» Вы действительно считаете, что все проблемы с обработкой ЧПТ решены? А зачем INTEL в железе реализует обработку ЧПТ в двоично-десятичном формате?
          Тут проскакивало что то про расчет на калькуляторе и на компе и что цифры не совпадают… А ничего что калькуляторы используют в основном двоично десятичный код для вычисления? Интересно, много людей, что здесь дискутирую вообще знают что это и почему используется?

          Чуть ваше я как раз объяснял вкратце с каким форматом работает калькулятор. Так что, кто не знал, видимо, уже знает.


          1. mmMike
            15.09.2016 09:59

            Вы действительно считаете, что все проблемы с обработкой ЧПТ решены?

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


            И все…
            Все остальное — это Вы уже сами за меня додумывает и высказываете.


            1. Innotor
              15.09.2016 10:14

              А вычислениям с плавающей точкой посвящены целые главы литературы по программированию. Включая и кучу примеров где проблемы и методы их решения рассматриваются.

              Вы считаете, что этой куче чего то не хватает? и поэтому
              привести примеры типичных классических ошибок программиста при работе данными в формате с «плавающей точкой» и как их избежать

              Если же все, что написано в статье, давно всем известно, то дайте ссылку, где эта проблема освещена.
              Извините, но просто поговорить, действительно не интересно.


              1. mmMike
                15.09.2016 10:29

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

                Я вначале подумал, что это такая тонкая шутка, как вступление к лекции "для чайников". Но Ваш последний комментарий показал, что это не было шуткой, а сказано на полном серьезе:


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

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


                Ну конспекты лекций я сканировать и отправлять не буду (если найду их еще).
                А так google: "проблемы с плавающей запятой учебник".


              1. Refridgerator
                15.09.2016 10:44

                Если же все, что написано в статье, давно всем известно, то дайте ссылку, где эта проблема освещена

                Первая же ссылка в google на запрос «floating point issues»:
                What Every Computer Scientist Should Know About Floating-Point Arithmetic


                1. Innotor
                  15.09.2016 16:14

                  К сожалению формат дискуссии не позволяет сделать подробный анализ выкладок в указанной вами статье. Но, если вы с ней знакомы, то, во-первых, в ней рассматривается частный случай:
                  «A relative error of b — 1 in the expression x — y occurs when x = 1.00...0 and y = .rrr..., where r = b — 1» Обозначения изменены, чтобы не вставлять картинки. Далее, доказывается справедливость этого высказывания и, чтобы избежать этой ситуации автор предлагает ввести защитную цифру, которая позволяет получить результат близкий к factor times e. Который характеризует ошибку вычислений и определяется как e=(b/2)b^(-p). Далее доказывается, что добавление защитной цифры делает относительную погрешность не хуже 2e.

                  Возвращаясь к моей статье. Для формата float: b=2, p=24, a=105.3256, b=105.32 e=0,00000006
                  a-b=0,000000024. Относительная ошибка 0,000000024/0,0056=0.0000043, что существенно больше 2e=0,00000012


                  1. vadimr
                    15.09.2016 16:38

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


                    1. Innotor
                      15.09.2016 17:34

                      У нас два точных числа a=105.3256 и b=105.32. Какова их абсолютная и относительная погрешность? Она равна нулю.Преобразуем их в двоичные числа и получаем приблизительный эквивалент точных значений. Чем меньше длинна двоичной мантиссы, тем менее точным становится десятичный эквивалент двоичного числа. При арифметических действиях над приблизительными числами всегда получаются приблизительные результаты. Абсолютная разность между полученным точным результатом и полученным приблизительным результатом дает абсолютную ошибку. Отношение абсолютной ошибки к точному числу дает относительную ошибку.

                      Относительная погрешность может получиться какая угодно, в зависимости от соотношения порядков результата и операндов

                      О каких соотношениях порядков результата и операндов идет речь? Желательно с примерами.


                      1. vadimr
                        15.09.2016 20:18

                        Если уж мы говорим о погрешностях, давайте изъясняться метрологически корректно. Может быть, в идеальном платоновском мире и существуют точные числа, но в реальности каждое значение имеет погрешность, связанную с различными причинами. В частности, всякое число, записанное в позиционной системе счисления, имеет погрешность представления, равную половине цены младшего разряда. Для числа 105.3256 абсолютная погрешность представления равна 0.00005, для числа 105.32 – 0.005. Разность этих чисел равняется 0.01 с погрешностью 0.00505, равной сумме погрешностей операндов.

                        Предполагая, что вместо второго числа Вы подразумевали 105.3200, можно заметить, что его абсолютная погрешность равна 0.00005, а разность чисел равна 0.0056 с абсолютной погрешностью 0.0001. Так как сама величина разности мала по абсолютной величине, то относительная погрешность разности оказывается велика, 0.0001/0.0056 = 0.0179.

                        Заметьте, это всё чисто аналитические выкладки, не имеющие пока никакого отношения к компьютеру и его представлению вещественных чисел.


                        1. Innotor
                          15.09.2016 20:42

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

                          Берем 5 яблок и отнимаем от них 2. Получаем 3? Или все же 3+- немножко? Какой ответ верен? Яблоки могут быть совершенно разные по размерам. Если мы считаем в столбик разность двух чисел 105.3256 и 105.32 мы получаем в ответе 0.0006. Или +- что то еще? Калькулятор не знает предыстории чисел с которыми имеет дело. Это вы должны интерпретировать полученные результаты.


                          1. vadimr
                            15.09.2016 20:50

                            3 плюс-минус немножко, конечно. Пока вы отнимаете яблоки, они истираются, или, наоборот, что-нибудь с рук на них налипает. Проще заметить, вычитая 2 тонны яблок из 5 тонн.

                            (Говоря о единичных яблоках, это, в практических целях, 5.0 и 2.0. Половину яблока ещё различаем при подсчёте количества, а 0.05 – уже нет.).

                            То, что вы и ваш калькулятор при вычитании 105.32 в столбик получаете ответ 0.0056, говорит только о том, что вы с калькулятором не учитываете погрешность вычислений, то есть считаете метрологически неверно. Инженеров, как правило, отучают вычитать 105.32 из 105.3256 ещё на первом курсе.


                            1. Innotor
                              15.09.2016 21:06

                              Пока вы отнимаете яблоки, они истираются, или, наоборот, что-нибудь с рук на них налипает.

                              Если какое-нибудь яблоко не прилипнет к рукам раздающего, они будут разложены ровно на две кучки по 5 и 3 в каждой.
                              То, что вы и ваш калькулятор при вычитании 105.32 в столбик получаете ответ 0.0056, говорит только о том, что вы с калькулятором не учитываете погрешность вычислений, то есть считаете метрологически неверно

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


                              1. vadimr
                                15.09.2016 21:07

                                Я не возражаю, но тогда вообще не надо говорить про погрешности.


                                1. Innotor
                                  15.09.2016 21:15

                                  Я бы про них и не говорил, если бы они не возникали в процессе вычислений дробных десятичных чисел инстументарием двоичной арифметики.


                                  1. vadimr
                                    15.09.2016 21:18

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


                                    1. Innotor
                                      15.09.2016 22:13

                                      У меня 3.00 детей в десятичном исчислении. Это истина. Это конечная запись, больше детей у меня не будет. Какие ложные предпосылки вы в этом усматриваете и какие выводы вы можете сделать? Вариант — дети не от меня, исключается:)
                                      А если серьезно, то точные числа существуют, а натуральные числа вообще связаны исключительно со счетными задачами. Это потом появились рациональные числа, в результате операций сравнения. Что больше (меньше) и во сколько раз. И даже для них, если числа соизмеримы, т.е. в них укладывается ровное количество меры, они будут точными. Не в смысле точности измерения, а в смысле количества мер, которые в них укладывается. Пример: эталон — человек обыкновенный. Сколько человек в кинотеатре? Скажем — 201. Это точное число или приблизительное?
                                      Вообще, понятий точности много. Большой энциклопедический политехнический словарь понятие точности определяет как «степень приближения истинного значения рассматриваемого процесса, вещества, предмета к его теоретическому номинальному значению» [ dic.academic.ru/dic.nsf/polytechnic/9524/ТОЧНОСТЬ ]. Для численных задач, мне кажется, это верное определение. Поэтому результат, полученный в вычислительном устройстве, тем точнее, чем он ближе к вычисленному столбиком вручную.
                                      Извините, если сделаете свой комент, скорее всего отвечу послезавтра. Уезжаю.


                                      1. vadimr
                                        16.09.2016 05:49

                                        Когда вы говорите о своих детях, то это их нумерация натуральными числами в вашем списке детей, а не измерение количества, поэтому в данном случае говорить о погрешности не приходится.

                                        Что касается точности, то в инженерном деле стараются не использовать этот термин, если стремятся к строгости терминологии. Хотя в вычислительной технике существуют понятия «одинарная точность», «двойная точность» и т.д., качественно характеризующие конкретные форматы представления чисел в компьютере.


                                        1. Innotor
                                          17.09.2016 09:05

                                          Когда вы говорите о своих детях, то это их нумерация натуральными числами в вашем списке детей, а не измерение количества, поэтому в данном случае говорить о погрешности не приходится.

                                          Считаю, все ли дети дома? Один, два, три. Так, общее количество — приблизительно 3!
                                          Хотя в вычислительной технике существуют понятия «одинарная точность», «двойная точность» и т.д., качественно характеризующие конкретные форматы представления чисел в компьютере.

                                          Не качественно, а количественно. В формате флоат можно представить точно до 7 значащих десятичных цифр. В формате дабл — до 16 десятичных цифр. И хотя, после перевода в десятичное представление дробного двоичного числа конечной длительности, вы можете получить бесконечную дробь, первые 16 значащих цифр десятичного числа, проконвертированного из 2 в 10 систему, будут точными.


                          1. DistortNeo
                            15.09.2016 21:52

                            Не путайте тёплое с мягким.
                            5 — число целое, хранится в целочисленном типе.
                            Хотите точные операции с дробями — используйте fixed point арифметику.


                            1. Innotor
                              15.09.2016 22:20

                              Хотите точные операции с дробями — используйте fixed point арифметику.

                              Это каким же образом формат fixed point обеспечит точность операций для чисел, скажем до 15 знаков после запятой? Зачем тогда ЧПТ?


                              1. DistortNeo
                                15.09.2016 22:47

                                Это каким же образом формат fixed point обеспечит точность операций для чисел, скажем до 15 знаков после запятой?
                                Зачем тогда ЧПТ?

                                Fixed point имеет фиксированную абсолютную точность, тогда как Floating point — фиксированную относительную точность.

                                64-битное целое может хранить до 18-19 разрядов, дальше сами думайте, куда пихать разделитель дробной части.


                                1. Innotor
                                  17.09.2016 08:46

                                  64-битное целое может хранить до 18-19 разрядов, дальше сами думайте, куда пихать разделитель дробной части.

                                  Во-первых, 64-битное целое двоичное число может обеспечить представление десятичного числа с точностью не выше 16 значащих цифр. А во-вторых, в целом числе отсутствует дробная часть, которая и приводит к описанным в статье неприятностям.


                            1. Refridgerator
                              16.09.2016 05:45

                              Хотите точные операции с дробями — используйте fixed point арифметику.

                              Для точных рациональных вычислений нужно использовать специальные средства, например, класс «ratio» в c++, который хранит отдельно числитель и знаменатель в целочисленном виде.


          1. Siemargl
            15.09.2016 17:27

            Инструкции BCD устарели и с переходом на x64 убраны.


      1. mmMike
        15.09.2016 09:07

        До сих пор помню, как мне гад препод, из вредности, срезал 5 (на 4) вопросом про разрядность регистра 80487 (FPU сопроцессор, если кто не помнит). На лекции его не ходил. Хотя каждый день на кафедре встречались.
        Одна 4-ка за 4 года.


        Базовые знание по работе комп. потрохов очень помогают. Меньше ситуаций "Это магия какая то… почему результат не верный".


  1. VaalKIA
    15.09.2016 13:07

    Я наконец-то понял, что хотел сказать автор.
    Сначала несколько пунктов:
    1. Никакие FP не имеют к сути статьи никакого отношения.
    2. Количество значащих разрядов и точность чисел тут тоже не при чём.
    Заголовок статьи должен был быть таким: «Фатальные ошибки двоичного представления дробных десятичных чисел.»

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


    1. DistortNeo
      15.09.2016 15:16
      +1

      Головная боль бухгалтера — как разделить миллион рублей поровну на трёх человек.
      Здесь даже точные десятичные вычисления не помогут.


      1. VaalKIA
        15.09.2016 16:16

        Пошёл делать лоботомию :-)))


      1. Innotor
        15.09.2016 18:23

        Я конечно далек от бухгалтерии, но может такое наивное решение пригодится. Создать графу «нераспределенные остатки» в которой учитывать копейки или доли копеек. Эти деньги временно хранить в банке. Когда следующий раз будете распределять миллион прибавьте к распределяемой сумме нераспределенные ранее остатки глядишь сумма и разделится. Если нет — опять остатки в банк.


        1. Refridgerator
          15.09.2016 18:48

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


          1. Innotor
            15.09.2016 19:25
            +1

            Ответ на этот вопрос как то прольет свет, на рассматриваемую проблему?


            1. Refridgerator
              15.09.2016 20:21

              Прольёт свет на контекст.


              1. Innotor
                15.09.2016 20:50

                Я так понимаю, к контексту у вас вопросов нет? Есть вопросы к социальному статусу автора? В зависимости от ответа на последний вопрос, вы согласитесь с контекстом или нет?
                Кстати, а что по поводу What Every Computer Scientist Should Know About Floating-Point Arithmetic? Я что-то не правильно посчитал?


                1. Refridgerator
                  15.09.2016 21:46

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


        1. DistortNeo
          15.09.2016 21:53

          Вам налоговая за эту копейку нервов на миллион выест.


          1. Innotor
            15.09.2016 22:21

            Вам видней.


        1. Andy_U
          16.09.2016 12:06

          Насколько я понимаю, в бухгалтерии имеются однозначные жесткие правила округления до копеек. Типа, как посчитать 13% от 123 руб 10 копеек.