Фатальные ошибки двоичной арифметики при работе с числами с плавающей точкой

    Среди всего разнообразия форматов представления действительных чисел в компьютерной технике особое место отведено формату чисел с плавающей точкой (ЧПТ), который запротоколирован в стандарте 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, можно сделать вывод, что использование двоичной арифметики для десятичных чисел с плавающей точкой может приводить к совершенно непредсказуемым результатам.
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 229

    • НЛО прилетело и опубликовало эту надпись здесь
        0
        Убрал
        +7
        Так это всё общеизвестные факты.

        Всё равно что написать статью «НЕВОЗМОЖНОСТЬ ПРЕДСТАВЛЕНИЯ ЦЕЛЫХ ОТРИЦАТЕЛЬНЫХ ЧИСЕЛ С ПОМОЩЬЮ ТЕКУЩЕЙ РЕАЛИЗАЦИИ ДВОИЧНОЙ СИСТЕМЫ СЧИСЛЕНИЯ В ЭЛЕКТРОННЫХ ВЫЧИСЛИТЕЛЬНЫХ МАШИНАХ.»
          +1
          В статье речь идет о результатах арифметических вычислений действительных десятичных чисел с использованием двоичной арифметики.
            0
            Я просто по аналогии привёл пример.
            В учебниках же пишут про float и double, что они не точные.

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

            из float в double
            ?cdbl(csng(0.18))
             0.180000007152557 
            
              –1
              В учебниках же пишут про float и double, что они не точные.

              Да, это всем известно. Но в статье речь идет не об ошибках преобразования десятичных чисел в двоичные и обратно, а к последствиям, к которым эти преобразования приводят при простейших арифметических вычислениях.
                0
                Согласен, хотя в очень старом учебнике об этом тоже было написано, и была даже пара примеров.
                  –1
                  К сожалению я не нашел этого очень старого учебника и поэтому ничего возразить не могу.
                    0
                    Ребятам даю пример:
                    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; 
                    

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

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


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

              Но почему же Excel использует неустойчивый алгоритм стандарта IEEE754 для сложения, вычитания и проч. Если на выходе мы имеем непредсказуемый результат?
                0
                Для скорости и экономии памяти конечно!
                Ведь есть библиотеки для точных вычислений. Но они, естественно, медленнее чем железная реализация. И железная реализация сделана такой какая она есть ради скорости и экономии памяти.
                  0
                  Если Вас устраивает экономия памяти, при которой вы получаете результат с относительной погрешностью 0.6% на одной операции, то конечно проблем не будет.
                  0,0056-0,005599976 =0,005599976/105.3256 ≈0,006*100=0.6%
                    0
                    Если нужна точность — используют или фиксированную точку или готовые библиотеки или пишут что-то самодельное.
                      0
                      А можно подробнее откуда такое число 0.6% относительной погрешности?
                      (0,0056 — 0,005599976) / 0,0056 ≈ 0.000004 относительной погрешности. Нет?
                        0
                        Да, вы правы, в пылу полемики неправильно посчитал.
                    +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
                    
                      0
                      Далее я делаю миллион вычислений по определенному алгоритму и должен быть уверен, что данная ошибка представления (да и все остальные ошибки, возникающие при каждом вычислении) в результате не накопятся и не превысят определенный порог.

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

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

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

                          А вы уверены, что такой неожиданный результат из придуманных нами, случайно не выскочит при контроле, например, за ядерной установкой?
                            0
                            В последнем примере вы сравниваете расчеты в Excel с вычислениями на калькуляторе, при этом каждое действие на калькуляторе округляете до 15 знака после запятой. Так?
                            В итоге результат Excel и калькулятор совпадают в 15 знаках после запятой. Я все правильно понимаю?
                            Простите, может я слишком хочу спать и неправильно интерпретирую ваши 18 значные дроби.
                              0
                              Правильно. Если вычислять по правилам арифметики с точностью до 15 знака, с калькулятором мы делаем все верно. Если вычислять до 15 знака после запятой в формате дабл, и значащие цифры считать от точки в ненормализованном числе, то хвост не мешает. Арифметические операции не приближают неверные цифры к точке. Но, если считать по количеству значащих десятичных цифр, или от точки, в нормализованном числе, то хвост попадает в область верных цифр или участвует в образовании младшей значащей десятичной цифры. Проблема в том, что отсечение хвоста в двоичном коде увеличивает хвост в десятичном представлении и приближает неверные десятичные цифры к точке. Чтобы все было корректно, надо на каждом операционном шаге десятичное представления результата округлять до нужного значения
                                0
                                Вот честно, два раза перечитал ваш комментарий и ничего не понял. Вы сравниваете два результата вычислений. Один провели сами с точностью до 10^(-15), другой доверили экселю. Получили совпадение в 15 знаках после запятой. И вам почему-то это не нравится. А вот я бы, наверное, не доверял скорее результату ваших вычислений с арифметическими округлениями, а не округлением до четного.
                                  0
                                  В соответствие с правилами арифметических операций, чтобы сложить/умножить/разделить два числа с точностью до N-го знака после запятой, каждое число должно быть округлено до N знаков. Делая арифметические операции над двоичными числами, которые приблизительно представляют исходные десятичные, мы не можем выполнить этого требования, т.к. уменьшение разрядности двоичного числа делает его менее точным в его десятичном представлении. Таким образом, мы имеем нарушения правил сложения десятичных приблизительных чисел, в результате которого мы получаем неверный результат.
                                    0
                                    1.25/0.25 = 5
                                    теперь, следуя вашей логике, я округляю до десятых, надеясь получить один правильный знак после запятой (N = 1)
                                    1.3/0.3 = 4.(3)
                                    Сколько верных знаков после запятой у меня получилось?
                                      0
                                        0
                                        Я что-то неправильно округлил?
                                        0
                                        Да, по правилам арифметики так и получается. Как говорится «что выросло, то выросло»
                                          0
                                          Я цитирую вас:
                                          В соответствие с правилами арифметических операций, чтобы сложить/умножить/разделить два числа с точностью до N-го знака после запятой, каждое число должно быть округлено до N знаков.

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

                                            В Вашем примере с ядерной установкой, десятичной системе просто неоткуда взяться.
                                              0
                                              Вся беда в том, что как только нам приходится делить два целых числа в десятичном или в двоичном коде, возникновение действительных, рациональных, дробных чисел неизбежно. Вопрос только в том, какую точность мы желаем получить при ограниченных аппаратных ресурсах.
                                              0
                                              Если бы мы использовали 16-ричную систему, то проблемы бы остались такими же.
                                                0
                                                Ну, проблемы неточности представления двоичных дробей не было бы.
                                                  0
                                                  Почему? Все равно округлять бы пришлось из-за ограничения на длину мантиссы.
                                                    0
                                                    Если бы в длину мантиссы по количеству знаков не уложились, то да. Но короткие шестнадцатиричные дроби кодируются точно, в отличие от некоторых десятичных, вроде 0.2.

                                                    Часть проблем автора поста связаны с тем, что не все конечные десятичные дроби имеют конечное двоичное представление.
                                                      0
                                                      16-ричные дробные числа, не переполняющие разрядную сетку мантиссы конечно могут быть точно представлены в двоичном коде. Это две соизмеримые системы счисления. Но, когда приходится делить два таких числа друг на друга мы опять можем получить бесконечную дробь:
                                                      0.9 ⁄ 0.7 = 1.49(249) в 16-ричной системе счисления.
                                                        0
                                                        Смотря что эти 0.2 означают. Если это именно точное значение, то логичнее его представлять как 1/5, а не 0.2.

                                                        Если же это результат физического измерения (масса, например), то точность измерения будет на порядки ниже точности округления. Если значение измерено с точностью до 1 знака, то мы можем смело 0.2 представить как 21/256, и ничего не потеряем.
                                  0
                                  В статье представлен пример, когда всего за три арифметические операции с числами формата doubl мы получили ошибку в младшем разряде равную 4. Этот пример легко продолжить. А вы говорите о миллионах операций.

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

                                      Это как? По моей логике, «увеличить в два раза аппаратный регистр» = «увеличить точность в 2 раза», в то время как в алгоритме Кэхэна ничего подобного не наблюдается. Там просто контролируется накопление погрешности. Опять же, с ограниченной точностью.
                                        0
                                        А алгоритм Кэхэна для того и придуман, чтобы увеличить точность вычислений в 2 раза.
                                          0
                                          Ничего подобного, абсолютная точность алгоритма совпадает с точностью переменной, в которой копится сумма. Относительная погрешность лучше — это да.
                                            0
                                            А вы можете обосновать, что точность увеличивается именно в 2 раза, а не, скажем, в полтора? Откуда вообще взялась эта цифра? Ни в русской, ни в английской википедии её нет.
                                              0
                                              Конечно же не в 2 раза, а в 10^N для десятичных чисел или в 2^N для двоичных чисел. Где N — количество разрядов, выделенное для учета дробных значений, которые вышли за пределы разрядной сетки операционной мантиссы. Учет этих значений уменьшает абсолютную погрешность и как следствие относительную.
                                                0
                                                Выглядит ещё менее убедительно. Если честно, выглядит так, как будто вы эту формулу взяли просто из головы. Как минимум потому, что в ней отсутствует количество суммируемых элементов. Кроме того из неё следует, что если для хранения накапливаемой погрешности вместо double взять long double, это приведёт к экспоненциальному росту точности. Что сомнительно. Ведь вычисления производятся по-прежнему в оригинальном формате.
                                                  0
                                                  В примере из Википедии для старших цифр десятичных чисел отведено 6 разрядов и столько же для погрешности. В общей сложности получается, что число представляется 6+6+12 разрядами. Каждый новый разряд увеличивает точность представления числа в 10 раз для десятичных чисел. Вот и получается, что за счет дополнительного программного регистра точность повысилась в 10^6 раз.
                                                    0
                                                    6+6=12
                                    0
                                    А вы не используйте алгоритмы, которые допускают такое накопление.
                                    Например, для обучения свёрточных нейронных сетей зачастую достаточно даже не float, а half-float (2 байта).
                                      0
                                      А я и не использую ,)
                                0
                                  0
                                  Десятичные числа (с ограниченным числом разрядов) — рациональные(т.е. лишь счетное подмножество вещественных числе), а не вещественные. Как, кстати, и двоичные.
                                    0
                                    Именно этот факт и приводит к тем ошибкам, о которых в статье говорится.
                                      +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)
                                        0
                                        Так в чем неточность? В термине «действительное число»? Да, я с вами согласен, это число рациональное. Почему-то в литературе, в том числе в стандарте IEEE754, дробные рациональные числа называются действительными. Я не стал менять эту традицию, т.к. для данного случая это не принципиально.
                                          +4
                                          Хорошо. Но теперь Вам стало понятно, что равенство (из Вашей статьи):
                                          0.06543455=1.00001100000001010001101〖2〗^(-4)

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

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

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

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

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

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

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

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

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

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

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

                                                0
                                                Я не понял, по названной нами причине ошибки при вычислениях имеются или нет? Я же про ошибки говорю, которые обусловлены несоизмеримостью систем счисления. Так что неверно в названии?
                                                  0
                                                  Это не ошибки, а погрешности. Т.е. не bug, а feature. И Вы правильно сейчас написали, что причина в невозможности точно преобразовать числа конечной длины из N-ричной системы счисления в M-ричную. Т.е. пока мы используем лишь одну систему счисления, проблемы нет. Но Ваша статья называется «Фатальные ошибки двоичной арифметики...».
                                                    –1
                                                    Вы напрасно поставили многоточие. Речь идет именно об ошибках (или погрешностях) при двоичных вычислениях с десятичными ЧПТ. Не буду спорить по поводу семантики понятий bug и feature, но на мой взгляд последствия очевидны, как в том, так и в другом случае.
                                                      +1
                                                      Вы напрасно поставили многоточие.

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

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

                                                      Ну нет внутри современных компьютеров (ну уровне железа) десятичных чисел с плавающей запятой. Только двоичные.
                                                        0
                                                        Да, я с Вами согласен. Надо было уточнить про десятичные числа. Боюсь уже поздно.
                                              0
                                              анекдот в том, что как раз скорость света (в м/с ) является целым числом (точно 299792458), через которое определяется метр.
                                                0
                                                Ваша правда. Неудачный я пример привел.
                                      +1
                                      Дело не только в формате представления чисел.

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

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

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

                                          Утверждается (например на stackoverflow), что с предварительным расширением до 80-бит и без него можно получить разные результаты вычислений.
                                            0
                                            Если мне мою карму не опустят ниже плинтуса, в следующем топике я постараюсь представить свое видение этого вопроса. А здесь еще раз повторюсь, расширение количества бит под двоичную мантиссу не помогает избежать ошибок вычисления. Это я попытался показать на двух форматах float и double.
                                          0
                                          x87 все вычисления производит в 80-битном расширенном формате, и для double и для float
                                            0
                                            И вот когда мы отбрасываем двоичный хвост, возвращаясь к double или float, тут и вырастает десятичный хвост, который все и портит.
                                              0
                                              В общем случае «хвост» в FPU уже есть, просто на выходе мы получаем меньшую точность этого хвоста. Математики, которым критичен этот хвост, и так в курсе того, что компьютер может хранить лишь конечное множество всех возможных значений, в курсе мантисс и экспонент, и в курсе различий между численным и аналитическим решением уравнения. Выводы делайте сами!
                                                0
                                                Ну, про точность хвоста, мы ничего не можем сказать, нам про него ничего не известно.
                                                Математики, которым критичен этот хвост, и так в курсе того, что компьютер может хранить лишь конечное множество всех возможных значений, в курсе мантисс и экспонент, и в курсе различий между численным и аналитическим решением уравнения
                                                .
                                                Но, позвольте, а как пользователю относиться к тому, что мы получаем на выходе Excel? Математики может и в курсе, что некоторые числа могут получаться неточными. А как пользователю определить, когда можно доверять результату, а когда нет?
                                                  +2
                                                  Позвольте, как мне прооперировать себя, не владея скальпелем? Срочно нужна статья о хирургии для обычных пользователей. Причём, написать её должен не хирург, а другой обычный пользователь, случайно отрезавший себе палец.
                                                    0
                                                    Ваш юмор я оценил. Но как быть с расчетами?
                                                      0
                                                      Считать, учитывать.
                                                        0
                                                        А как учитывать, если из заявленной точности — 15 достоверных цифр после запятой для нормализованных чисел формата double, верными оказываются только 7?
                                                          0
                                                          Озвучьте вашу версию ответа, пожалуйста.
                                                            0
                                                            Думаю, ответ такой. Не использовать в расчетах с действительными (рациональными) числами двоичную арифметику в том виде, как она реализована в стандарте. Причем не важно, как она реализована, на аппаратном или на программном уровне. Вся беда в том, что проблемы начинаются, когда десятичное число содержит дробную часть. Целые числа не приводят к ошибкам представления. Поэтому, если представлять числа в виде ЧПТ с целочисленной мантиссой, эти проблемы не возникают. Возникают другие проблемы, но они не системные и преодолимы. Попытка ответить на ваш вопрос была предпринята в моем топике https://habrahabr.ru/post/272251/. В комментариях можно найти ссылку на PDF-файл.
                                                              0
                                                              Теперь стало понятно, где у вас «болит» (ранее эту статью не читал). Думаю, вам стОит не евангелизированием своих идей заниматься, а реализовать вашу альтернативу в виде библиотеки и сравнить её на каких-нибудь бенчмарках (поиск ранга матриц размером триллиард на триллиард, рассчёт какой-нибудь несущей конструкции с триллиардом элементов и т.п. — и по скорости, и по точности, и по стабильности этой точности) с существующими библиотеками и решениями. Мне кажется, что среди этих альтернативных решений вы и для себя откроете множество интересных подходов к контролю точности вычислительных задач.
                                                                0
                                                                Простите, я не понял, а как же быть с ошибками вычислений при самых простых арифметических операциях? Или они не актуальны при поиске ранга матриц размером триллиард на триллиард?
                                                                  0
                                                                  Так же, как «были» до этого.
                                                    +3
                                                    А Вы не используйте Excel для расчётов ядерного реактора и всё будет хорошо :)
                                                    Честно-честно.
                                                      0
                                                      В статье речь идет ни об ошибках в Excel, а проблемах работы с ЧПТ, которые представлены в двоичном коде. Excel был взят для иллюстрации.
                                                      0
                                                      Вот программка, которую по моей просьбе написал Дмитрий Пикулик.
                                                      /*
                                                      * 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

                                                      Сравните с тем, что получится на калькуляторе.
                                                        +2
                                                        Задайте себе вопрос, почему, при математических расчётах ЧПТ на ПК сравнение выполняют не так:
                                                        double a = f1(), b = f2();
                                                        if ( a != b ) {… }
                                                        а так:
                                                        double a = f1(), b = f2();
                                                        if ( fabs( a — b ) > EPSILON ) {… }
                                                        и на сколько полезна ваша серия статей (аж в год длинной) про недостатки текущего стандарта?
                                                        Всё известно, кто хочет точности — пользуется специальными библиотеками.
                                                          –1
                                                          Разница между двумя числами double a = 0.66 и double b = 0.659999996423721 составляет 0,000000003576279. Вопрос, какое должно быть EPSILON, чтобы убедиться, что это число равно числу 0.00000000357627905067659

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


                                                          Ну, статья не про IEEE754, а об ошибках, которые получаются в результате арифметических операций при использовании двоичной арифметики для десятичных ЧПТ. Эти ошибки не совсем очевидны, так как в большом количестве случаев результаты вычислений будут верными при использовании стандарта. Но где гарантия, что мы не столкнемся именно с теми немалочисленными ситуациями, когда будут получены совершенно неприемлемые результаты.
                                                            +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
                                                              0
                                                              По-моему кавычки в словах «всего лишь» надо убрать, т.к. 24 двоичных разряда мантиссы формата float обеспечивают представление десятичного числа с точностью до 7 знаков после запятой.
                                                                0
                                                                Кавычки тут больше для привлечения внимания, допустимая точность для float не 7, а пять знаков после запятой, именно это и отражает постоянная FLT_EPSILON
                                                                  0
                                                                  Если мы говорим о представлении десятичного числа в двоичном коде, то 24 двоичных разряда мантиссы нормализованного числа позволяют представить любое десятичное число с 7 верными цифрами. Почему же вы ограничились 5 знаками?
                                                                    0
                                                                    Если грубо — 5 знаков это допустимая точность вычислений для float, 9 — double. Любые вычисления, требующие большей точности требуют смены типа переменных. Например, если мы считаем некоторую величину где будут учитываться 5 и 6 знаки после запятой — float уже не подходит, его точности не хватает, выбираем double. Если нужна точность более 9 знаков — дабл так же не подходит. Это основы программирования с ЧПТ, учат ещё когда проходят эти числа.
                                                                    Вы сейчас боретесь или с ветряными мельницами.
                                                                      0
                                                                      Посчитайте в формате float, чему это равно: 1234,567-1234,56=?
                                                                      У меня получилось 0.0069580
                                                                        0
                                                                        #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 все знают.
                                                                          0
                                                                          Да это уже на троллинг со стороны топикстартера похоже, если честно. Когда вы записали 1234.567 во float, вы уже ошиблись в 5 десятичном знаке после запятой. То же самое с 1234.56.
                                                                            0
                                                                            Так и я о том же. Как только вы преобразовали десятичное число к двоичному, вы ошиблись не в 5 знаке, а в 10000… знаке. Так как получили бесконечную дробь.
                                                                              0
                                                                              В пятом десятичном знаке.
                                                                                0
                                                                                В самом большом числе из приведенных всего 3 знака после запятой (1234.567). А вот после конвертации оно становится
                                                                                1234.56710 = 1.0011010010.100100010010011011101001〖∙2〗^10
                                                                                =1234.5670166015625
                                                                                Так про какой 5 знак идет речь?
                                                                                  0
                                                                                  Когда вы записали 1234.567 во float, вы уже ошиблись в 5 десятичном знаке после запятой.
                                                                                    0
                                                                                    Остальные я не считал: 7->(4->8->6->2->4). И это неважно. При ошибке в пятом десятичном знаке после запятой мы имеем 8 правильных десятичных знаков в представлении исходного числа. Большего мы от float и не ждем.
                                                                                      0
                                                                                      Ошибся не я, а природа, которая все это устроила. Еще раз повторюсь, речь идет не о точности конвертации чисел из одной системы счисления в другую, а о результатах вычислений.
                                                                          0
                                                                          1) И какова относительная погрешность?
                                                                          2) Вы снова передергиваете и приводите числа, не представимые в конечном двоичном коде.
                                                                          3) Какова относительная погрешность для чисел, которые вы в реальности записали в память?
                                                                            0
                                                                            1) И какова относительная погрешность?

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

                                                                            Я все время именно о таких числах и веду речь. Представимые числа — точные и с ними проблем нет до тех пор пока в результате арифметических действий они не превращаются в приблизительные.
                                                                              0
                                                                              В данном конкретном случае какова относительная погрешность вычислений?
                                                              +1
                                                              Не совсем так. Скорее, так:
                                                              |a-b| < epsilon*max(|a|, |b|)
                                                              
                                                                0
                                                                Спасибо, не знал этого способа.
                                                                  0
                                                                  Смыл в том что расстояние между соседними машинными числами в формате IEEE754 зависит от их абсолютной величины. Например, следующее за 10^8 во float будет 10^8+8. Поэтому абсолютная точность, скажем, 0.1 при таких порядках будет в принципе недостижима.
                                                                    0
                                                                    Я не совсем понял. У нас рассматриваются все числа, у которых один десятичный порядок.
                                                                      0
                                                                      я уточнил, зачем нужно проверять на равенство считая относительную погрешность, а не абсолютную
                                                                        0
                                                                        Я про проверку на равенство советов не давал
                                                                          0
                                                                          Так исходный мой комментарий и не вам был адресован.
                                                                            0
                                                                            Извините, закопался в коментах.
                                                      0
                                                      в FPU есть флаг точности с возможными режимами:

                                                      00 – 24 бита мантисса, 7 бит порядок;
                                                      10 – 53 бита мантисса, 10 бит порядок (двойная точность);
                                                      11 – 64 бита мантисса, 15 бит порядок (расширенная точность).
                                                        0
                                                        И что из этого следует?
                                                          0
                                                          Это было уточнение комментария «x87 все вычисления производит в 80-битном расширенном формате, и для double и для float».

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

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


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

                                                        В эксплуатационной документации к первым ЭВМ так и писали, что машина получает не сам результат математической операции, а доступное приближение к нему. Потом как-то все привыкли, и писать перестали.
                                                          –1
                                                          Используя тот или иной инструмент, я должен знать, какую точность гарантирует этот инструмент. Если для double гарантирована точность до 15 десятичных знаков после запятой для нормализованных чисел, а по факту я имею всего 7, то как можно использовать этот инструмент?
                                                            +1
                                                            а где написано именно про гарантированную точность?
                                                              0
                                                              В каком примере вы имеете 7?
                                                                0
                                                                См. в статье, где приведен пример для вычитания чисел в формате double.
                                                                  +1
                                                                  0,789012345005176
                                                                  А мы ожидали получить число: 0, 789012345
                                                                  Погрешность меньше допустимой 1Е-9, так что всё укладывается в рамки допустимого.
                                                                    0
                                                                    Там нет примера, только Ваши утверждения.

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

                                                                    d = 0.005600

                                                                    Если Эксель вам врёт, то причём тут машинная арифметика? Он вообще с символьными данными работает.
                                                                      0
                                                                      Посмотрите в комментариях чуть выше. Там приведен пример для формата double.
                                                                        0
                                                                        Вы можете конкретно написать пример? Из Ваших реплик ничего не понятно.
                                                                          0
                                                                          Не могу, хабр. ограничивает ресурсы. Пишите в личку.
                                                                            0
                                                                            Если Вы имеете в виду свой комментарий “Вот программка, которую по моей просьбе написал Дмитрий Пикулик”, то в этой программе:

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

                                                                            2) после вставки определения “double x;”, результат, выдаваемый программой, соответствует теоретическому с точностью до 16 десятичных цифр, что отвечает точности формата double.
                                                                              0
                                                                              Посчитайте разность: 123456,789012345-123456.
                                                                                0
                                                                                0.789012345005176, разница в 18-м десятичном знаке.
                                                                                  0
                                                                                  123456,789012345-123456=0,789012345. Согласно вычислениям на калькуляторе виндоуз.
                                                                                    0
                                                                                    Ну берите какой-нибудь язык PL/I и тип decimal fixed, если вас интересует точное представление десятичных чисел. Как верно указано в соседней ветке, настоящие физические параметры не имеют точного представления ни в одной системе счисления, поэтому совершенно неважно, как их записывать. Главное, чтобы погрешность алгоритма соответствовала требованиям решаемой задачи.
                                                                                      0
                                                                                      Вопрос в цене-времени-сложности. Нет таких калькуляторов, которые работали бы на указанных вами принципах.
                                                                                        0
                                                                                        Каких именно принципах? Практика программирования показала, что точное представление десятичных чисел никому не нужно, кроме бухгалтеров, которым достаточно целых копеек. Поэтому от десятичных дробей в современных широко распространённых системах программирования отказались. Остальные вещественные числа – это приближения иррациональных значений (так как мощность множества иррациональных чисел бесконечно больше мощности множества рациональных, то вероятность точно попасть в рациональное значение равна нулю).
                                                                                          0
                                                                                          Причём, так как изначально практически все источники вещественных цифровых значений в современном мире имеют двоичную природу, то представление чисел в десятичной системе их только портит.

                                                                                          Рано или поздно человечество перейдёт к шестнадцатиричной в быту.
                                                                                            0
                                                                                            Ну, дай то Бог!
                                                                        0
                                                                        a = 123456.789012345-123456.0;
                                                                        printf («a = %.15lf\n», a);

                                                                        a = 0.789012345005176

                                                                        Кто вам сказал, что Эксель работает с числами в формате double и в соответствии с IEEE 754?
                                                                          0
                                                                          Microsoft
                                                                            +1
                                                                            К нему и предъявляйте претензии, а не к формату double. То, что в Экселе много залепух в арифметике, давно известно.
                                                                            0
                                                                            Microsoft Excel was designed around the IEEE 754 specification to determine how it stores and calculates floating-point numbers.

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

                                                                              Я свои примеры даю на языке Си с использованием арифметики IEEE 754.
                                                                    0
                                                                    Курс дзена молодого бойца: попробуйте перемножить, а тем более уж поделить Точно одно-двух байтные целые числа на AVR.
                                                                      0
                                                                      Мастер! Попробуйте с точностью до 6 знака после запятой найти частное 2/3 в двух разрядном калькуляторе.
                                                                        0
                                                                        Я никого не хотел обижать. Просто AVR может ошибаться уже в 4-х значных числах, и без запятой.
                                                                          0
                                                                          Простите, но при чем тут AVR? Да, там нет сопроцессора, поэтому все операции с плавающей запятой производятся программно, это может сделать сам компилятор или использовать библиотечные реализации (http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_math).
                                                                          Однако, вы можете реализовать все операции самостоятельно, с любой точностью, какой пожелаете, ограничиваясь объемом памяти кристалла, конечно же.
                                                                            0
                                                                            На самом деле да, все как Вы сказали, я имел в виду скорее компилятор. Хотя не могу сказать по всем, но avr-gcc (самый популярный) и icc (очень редкий) генерируют неточный код. И у меня даже есть маленькая функция для быстрого и более точного деления на 10 (каждый раз при отображении чисел нужна).

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

                                                                            P.S. еще раз: я про целые числа, не про числа с плавающей запятой. Если даже в операциях с целыми числами могут быть такие ошибки вычислений, то что уж говорить про числа с плавающей запятой. И стоит добавить что у чисел с плавающей запятой неточность ожидаемая, то неточность при использовании целых чисел может застать в расплох. И да, я про переполнения конечно же.
                                                                              0
                                                                              Как можно ошибиться при умножении? Вы о чем вообще? Команда FMUL предназначена для операций с фиксированной запятой она сдвигает результат влево чтобы запятая результата оказалась на том же месте, что и у входных аргументов.
                                                                              Это значит, что если попытаться с её помощью перемножить два целых числа вы получите число в два раза большее чем ожидали.
                                                                                +1
                                                                                Ныряю в пепел, стыд и позор. Про FMUL я просто слышал и не правильно понял, признаю (я С-шник). Но и в остальном я не могу воспроизвести ничего. Даже ошибку в делении. Заминусуйте начальные комментарии чтобы никто ересь не читал. С чего я это все взял — не знаю, может раньше так было, а может у меня повреждение памяти, жаль с ECC нельзя поставить.
                                                                            0
                                                                            Для получения точного результата, при умножении целых беззнаковых чисел, необходимо, чтобы результирующий регистр имел число разрядов не менее суммы разрядов сомножителей.
                                                                              0
                                                                              Я не про переполнения конечно, иначе этот диалог, начатый мной, не имел бы смысла. Сходу не найду, но вот например есть калькулятор fmul calc — позволяющий смотреть где ошибается микроконтроллер avr при использовании этой инструкции (аппаратная инструция целочисленного умножения, принимает два байта, результат записывается в двухбайтовую переменную). Не знаю на сколько точен калькулятор, но эти ошибки имеют место быть.
                                                                        +2
                                                                        Что автор-то хочет, мне — не понятно…
                                                                        По моему, у него, каша в голове: Excel, какие-то листинги программы, обещания 7 знаков после запятой наследственной грамотой…
                                                                        Вы сами-то понимаете о чём речь?
                                                                        Если вы показываете листинг программы, где написанна десятичная константа, то она не десятичная, это всего лишь соглашение, точно так же как кодировка листинга (может быть в utf8, но это не значит, что print(«hello world») небудет в ANSI) и вывода программы. Если в листинге написано a := 0.6, почему вырешили, что в компьютере материализуется довоичнодесятичный аппаратный блок арифметики, а не разумное: «пишите как удобно, компилятор сам преобразует»?
                                                                        Значит вычёркивайте вообще все слова «десятичные» из текста.
                                                                        Почему вы утверждаете что при точности в 5 знаков результат сложения тоже 5 знаков? Теория говорит, что будет уже 4 точных знака и не надо делать выпученные глаза и тыкать нас в это носом, идите и читайте книжки.
                                                                          0
                                                                          Уважаемый! Нельзя ночью писать коменты, Судя по всему, при изложении своих мыслей, вы пользуетесь принципом «пишите как удобно, компилятор сам преобразует».
                                                                          Если по существу,
                                                                          Почему вы утверждаете что при точности в 5 знаков результат сложения тоже 5 знаков?

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

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

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


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

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

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

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


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


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


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

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

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

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

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


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

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

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

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

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


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

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


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

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

                                                                                          Первая же ссылка в google на запрос «floating point issues»:
                                                                                          What Every Computer Scientist Should Know About Floating-Point Arithmetic
                                                                                            0
                                                                                            К сожалению формат дискуссии не позволяет сделать подробный анализ выкладок в указанной вами статье. Но, если вы с ней знакомы, то, во-первых, в ней рассматривается частный случай:
                                                                                            «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
                                                                                              0
                                                                                              Чисто формально, абсолютная погрешность разности равна сумме абсолютных погрешностей уменьшаемого и вычитаемого, и это никак не связано со способом вычислений и представления чисел. Относительная погрешность может получиться какая угодно, в зависимости от соотношения порядков результата и операндов.
                                                                                                0
                                                                                                У нас два точных числа a=105.3256 и b=105.32. Какова их абсолютная и относительная погрешность? Она равна нулю.Преобразуем их в двоичные числа и получаем приблизительный эквивалент точных значений. Чем меньше длинна двоичной мантиссы, тем менее точным становится десятичный эквивалент двоичного числа. При арифметических действиях над приблизительными числами всегда получаются приблизительные результаты. Абсолютная разность между полученным точным результатом и полученным приблизительным результатом дает абсолютную ошибку. Отношение абсолютной ошибки к точному числу дает относительную ошибку.
                                                                                                Относительная погрешность может получиться какая угодно, в зависимости от соотношения порядков результата и операндов

                                                                                                О каких соотношениях порядков результата и операндов идет речь? Желательно с примерами.
                                                                                                  0
                                                                                                  Если уж мы говорим о погрешностях, давайте изъясняться метрологически корректно. Может быть, в идеальном платоновском мире и существуют точные числа, но в реальности каждое значение имеет погрешность, связанную с различными причинами. В частности, всякое число, записанное в позиционной системе счисления, имеет погрешность представления, равную половине цены младшего разряда. Для числа 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.

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

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

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

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

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

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

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

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

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


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

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

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


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

                                                                                                                      Я же выше говорил только о том, что “одинарная точность” и “двойная точность” – это некое условное обозначение форматов представления чисел, а не то, что есть некая формальная метрика “точность”, различающаяся ровно в два раза.

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

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

                                                                                                                        Я писал: «И хотя, после перевода в десятичное представление дробного двоичного числа конечной длительности, вы можете получить бесконечную дробь».
                                                                                                                        0.5(10)= 0.1(2), 0.625(10)=0.101(10) и т.д. Это точные равенства.
                                                                                                                        Математика создавалась не для решения инженерных задач. Это для решения инженерных задач привлекается математика.
                                                                                                                          0
                                                                                                                          Детей заперли в кладовке, а состриженные ногти остались снаружи. Чешуйки кожи постоянно отслаиваются. Всё имеет определённую точность рассмотрения.
                                                                                                                            0
                                                                                                                            Извините, думал это будет понятно без уточнения. В скобках указаны системы счисления, а не периодичность дробей.
                                                                                                                            Чешуйки кожи постоянно отслаиваются. Всё имеет определённую точность рассмотрения

                                                                                                                            Я все таки не пойму, какое количество детей заперто в кладовке? За единицу измерения берем голову или килограммы?.. Или мы не имеем права определять количество детей по головам? Тогда, если определяем только по массе, то, простите, из туалета только что вышло 45,46 кг. Васи:), а заходило 45,8. Посчитал по головам, получилось — ровно одна. Сколько Вась мы имеем в наличии? По вашему, где-то 45(+-1) кг. А он растет и становится его уже 92 кг. Было в кГ. 45,46 Васи, а стало в два раза больше — 92кг. или 92(+-)кг. Сколько взрослых Вась мы имеем?
                                                                                                                              0
                                                                                                                              Про скобки сначала невнимательно прочитал. Редкая нотация, поэтому без вычислений контекст не понятен. Потом понял и исправил.

                                                                                                                              Что касается детей в кладовке, то всё правильно, сначала надо определиться с тем, что и как мы измеряем. А когда вы под конец в точности определения дойдёте до квантовых величин, то придётся учесть, что предметы, кажущиеся нам материальными объектами, на самом деле – просто локализации плотности распределения вероятностей, и нахождение Васи в кладовке хотя и очень близко к единице, но с очень небольшой вероятностью он находится в любом другом месте во Вселенной.
                                                                                                                                0
                                                                                                                                Извините, изотерикой не занимаюсь, поэтому ничего возразить не могу.
                                                                                                                                  0
                                                                                                                                  Это физика вообще-то.
                                                                                                                                    0
                                                                                                                                    предметы, кажущиеся нам материальными объектами, на самом деле – просто локализации плотности распределения вероятностей

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