Является ли наиболее значительная десятичная цифра точностью, которая может быть преобразована в двоичную и обратно в десятичную без потери значимости 6 или 7.225?

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

⌊ (N-1) log 10 (2) ⌋ = 6 десятичных цифр (Single-precision)

и

N log 10 (2) & асимп. 7.225 десятичных цифр (Single-precision)

Где N = 24 Значительные биты (Single-precision)

Первая формула находится в верхней части страницы 4 " IEEE Standard 754 для двоичной арифметики с плавающей запятой", написанной профессором W Кахан.

Вторая формула найдена в статье Википедии " Формат с плавающей запятой с одинарной точностью в разделе IEEE 754 с двойной точностью, точечный формат: binary32.

По первой формуле профессор В. Кахан говорит

Если десятичная строка с не более 6 sig. dec. преобразуется в Single, а затем преобразуется обратно в одно и то же число sig. Декабре, то окончательная строка должна соответствовать оригиналу.

Для второй формулы, Wikipedia говорит

... общая точность 24 бита (эквивалентно log 10 (2 24) ≈ 7.225 десятичных цифр).

Результаты обеих формул (6 и 7,225 десятичных цифр) различны, и я ожидал, что они будут одинаковыми, поскольку я предположил, что они оба предназначены для представления наиболее значимых десятичных цифр, которые могут быть преобразованы в двоичные и с плавающей запятой затем преобразуется обратно в десятичное с тем же числом значащих десятичных цифр, с которого он начинался.

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

Ответы

Ответ 1

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

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


Почему эти два числа отличаются...

Числа 6 и 7.225 отличаются друг от друга, потому что они определяют две разные вещи. 6 - это самые десятичные цифры, которые могут быть округлены. 7.225 - приблизительное число десятичных цифр для 24-битного двоичного целого числа, поскольку 24-битовое двоичное целое число может иметь 7 или 8 десятичных цифр в зависимости от его конкретного значения.

7.225 было найдено с использованием специальной бинарной целочисленной формулы.

d spec= b & middot; log 10 (2)                 (д <суб > спецификациисуб >= конкретные десятичные цифры, b = бит)

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

d min= ⌈ (b-1) & middot; log 10 (2) ⌉   д <суб > минсуб >= минимальные десятичные числа, b = бит, ⌈x⌉ = наименьшее целое ≥ x)

d max= ⌈b & middot; log 10 (2) ⌉             (д <суб > макссуб >= максимальные десятичные числа, b = бит, ⌈x⌉ = наименьшее целое ≥ x)

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

Все это хорошо и хорошо, но вы можете спросить: почему 6 десятичных цифр для конверсии в оба конца, если вы говорите, что диапазон десятичных цифр для 24-битного номера равен 7-8?

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

Каждое десятичное целое имеет точное значение в двоичном формате. Однако то же самое нельзя сказать для каждого десятичного числа с плавающей запятой. Возьмите .1, например. .1 в двоичном выражении - это число 0.000110011001100..., которое является повторяющимся или повторяющимся двоичным. Это может привести к ошибке округления.

Кроме того, для представления десятичного числа с плавающей запятой требуется еще один бит, чем для десятичного целого числа, равного значению. Это связано с тем, что числа с плавающей запятой более точны, чем ближе они к 0, так и менее точные, чем дальше от 0. Из-за этого многие числа с плавающей запятой приближаются к минимальному и максимальному значениям (e min= -126 и e max= +127 для одиночной точности) теряют 1 бит точности из-за ошибки округления. Чтобы увидеть это визуально, посмотрите Что каждый программист должен знать о плавающей запятой, часть 1, написанной Джошем Хаберманом.

Кроме того, есть, по крайней мере, 784,757 положительные семизначные десятичные числа, которые не могут сохранить свое первоначальное значение после конверсии в оба конца. Примером такого числа, которое не может выжить в обратном направлении, является 8.589973e9. Это наименьшее положительное число, которое не сохраняет свое первоначальное значение.

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

d max= ⌊ (b-1) · log 10 (2) ⌋   (d <югу > тахсуб >= максимальные десятичные числа, b = бит, ⌊x⌋ = наибольшее целое число ≤ x)

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


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

18 июня 2017 года. Обновление:. Я хочу добавить ссылку на новую статью Рика Ригана, которая идет более подробно и, на мой взгляд, лучше отвечает на этот вопрос, чем любой предоставленный здесь ответ. Его статья " Десятичная точность двоичных чисел с плавающей запятой" и может быть найдена на его веб-сайте www.exploringbinary.com.

Ответ 2

Это говорят о двух немного разных вещах.

Цифры 7.225 1 - это точность, с которой число может храниться внутри. Например, если вы сделали вычисление с номером двойной точности (так что вы начинали с чего-то вроде 15 цифр точности), то округлили его до одного номера точности, точность, которую вы бы оставили в этой точке, была бы приблизительно 7 цифр.

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

Итак, допустим, что я начинаю с числа вроде 1.23456789 в виде строки, а затем конвертируем его в float32, а затем преобразуем результат обратно в строку. Когда я это сделал, я могу ожидать, что 6 цифр будут точно соответствовать. Седьмая цифра может быть округлена, поэтому я не могу ожидать, что она будет соответствовать (хотя она, вероятно, будет +/- 1 исходной строки.

Например, рассмотрим следующий код:

#include <iostream>
#include <iomanip>

int main() {
    double init = 987.23456789;
    for (int i = 0; i < 100; i++) {
        float f = init + i / 100.0;
        std::cout << std::setprecision(10) << std::setw(20) << f;
    }
}

Это создает таблицу, подобную следующей:

     987.2345581         987.2445679         987.2545776         987.2645874
     987.2745972         987.2845459         987.2945557         987.3045654
     987.3145752          987.324585         987.3345947         987.3445435
     987.3545532          987.364563         987.3745728         987.3845825
     987.3945923          987.404541         987.4145508         987.4245605
     987.4345703         987.4445801         987.4545898         987.4645386
     987.4745483         987.4845581         987.4945679         987.5045776
     987.5145874         987.5245972         987.5345459         987.5445557
     987.5545654         987.5645752          987.574585         987.5845947
     987.5945435         987.6045532          987.614563         987.6245728
     987.6345825         987.6445923          987.654541         987.6645508
     987.6745605         987.6845703         987.6945801         987.7045898
     987.7145386         987.7245483         987.7345581         987.7445679
     987.7545776         987.7645874         987.7745972         987.7845459
     987.7945557         987.8045654         987.8145752          987.824585
     987.8345947         987.8445435         987.8545532          987.864563
     987.8745728         987.8845825         987.8945923          987.904541
     987.9145508         987.9245605         987.9345703         987.9445801
     987.9545898         987.9645386         987.9745483         987.9845581
     987.9945679         988.0045776         988.0145874         988.0245972
     988.0345459         988.0445557         988.0545654         988.0645752
      988.074585         988.0845947         988.0945435         988.1045532
      988.114563         988.1245728         988.1345825         988.1445923
      988.154541         988.1645508         988.1745605         988.1845703
     988.1945801         988.2045898         988.2145386         988.2245483

Если мы рассмотрим это, мы увидим, что первые шесть значащих цифр всегда следуют за рисунком точно (т.е. каждый результат точно равен 0,01 больше, чем его предшественник). Как мы видим в исходном double, значение фактически равно 98x.xx456, но когда мы конвертируем float с одной точностью в десятичную, мы можем видеть, что цифра 7 th часто не будет вернемся правильно - поскольку последующая цифра больше 5, она должна округлить до 98x.xx46, но некоторые из значений не будут (например, от второго до последнего элемента в первом столбце 988.154541, который будет округлен, а не вверх, поэтому мы получим 98x.xx45 вместо 46. Таким образом, даже если значение (как хранится) точно соответствует 7 цифрам (плюс немного), к тому времени мы округляем значение через преобразование в десятичную и обратно, мы не можем больше зависеть от этой седьмой цифры (даже если есть достаточная точность, что она будет намного чаще, чем нет).


1. В основном это означает 7 цифр, а цифра 8 th будет немного более точной, чем ничего, но не очень много - например, если мы переводим из двойника 1.2345678, цифры точности .225 означают, что последняя цифра будет иметь около +/-.775 от того, что началось там (тогда как без .225 цифр точности было бы в основном +/- 1 того, что было начато там).

Ответ 3

Имейте в виду, что они являются одними и теми же формулами. Помните свою личную школьную математическую книгу:

    Log(x^y) == y * Log(x)

Это помогает фактически вычислить значения для N = 24 с вашим калькулятором:

  Kahan's:      23 * Log(2) = 6.924
  Wikipedia's:   Log(2^24)  = 7.225

Кахан был вынужден урезать 6.924 до 6 цифр из-за floor(), bummer. Единственное фактическое различие заключается в том, что Кахан использовал 1 меньшую точность.

Довольно трудно догадаться, почему профессор мог полагаться на старые заметки. Написано перед IEEE-754 и не учитывает, что 24-й бит точности предоставляется бесплатно. Формат использует трюк, самый старший бит значения с плавающей запятой, который не равен 0, всегда 1. Поэтому его не нужно хранить. Процессор добавляет его обратно, прежде чем он выполнит вычисление. Превращение 23 бит сохраненной точности в 24 эффективной точности.

Или он принял во внимание, что преобразование из десятичной строки в двоичное значение с плавающей запятой порождает ошибку. Многие хорошие округлые десятичные значения, например 0,1, не могут быть полностью преобразованы в двоичные. Он имеет бесконечное количество цифр, равно как и 1/3 в десятичной форме. Тем не менее, он генерирует результат, который отключается на +/- 0,5 бит, достигаемый простым округлением. Таким образом, результат будет точным до 23.5 * Log (2) = 7.074 десятичных цифр. Если он предположил, что процедура преобразования является неуклюжей и не имеет правильного раунда, результат может быть отключен на +/- 1 бит и N-1 является подходящим. Они не неуклюжи.

Или он думал, как типичный ученый или (не дай бог) бухгалтер, и хочет, чтобы результат вычисления преобразовывался обратно в десятичный. Например, вы получите, когда вы тривиально ищете семизначное десятичное число, чья конверсия назад и вперед не дает такого же числа. Да, это добавляет еще одну ошибку +/- 0,5 бит, суммируя до 1 бит общей ошибки.

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