Если правило значащих цифр означает что-то совершенно другое в другой области, продолжайте :-)

ы печатаете двойной поток, чтобы при чтении он не терял точность?

Я пытался:

std::stringstream ss;

double v = 0.1 * 0.1;
ss << std::setprecision(std::numeric_limits<T>::digits10) << v << " ";

double u;
ss >> u;
std::cout << "precision " << ((u == v) ? "retained" : "lost") << std::endl;

Это не сработало, как я ожидал.

Но я могу повысить точность (что меня удивило, так как я думал, что цифры10 - это максимум, который требуется).

ss << std::setprecision(std::numeric_limits<T>::digits10 + 2) << v << " ";
                                                 //    ^^^^^^ +2

Это связано с количеством значащих цифр, и первые две не учитываются в (0,01).

Так кто-нибудь смотрел на точное представление чисел с плавающей точкой? Какое точное магическое заклинание на потоке мне нужно сделать?

После некоторых экспериментов:

Беда была с моей оригинальной версией. После десятичной точки в строке были незначимые цифры, которые влияли на точность.

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

ss << std::scientific
   << std::setprecision(std::numeric_limits<double>::digits10 + 1)
   << v;

Это все еще не объясняет необходимость +1, хотя.

Кроме того, если я распечатываю число с большей точностью, я получаю распечатку с большей точностью!

std::cout << std::scientific << std::setprecision(std::numeric_limits<double>::digits10) << v << "\n";
std::cout << std::scientific << std::setprecision(std::numeric_limits<double>::digits10 + 1) << v << "\n";
std::cout << std::scientific << std::setprecision(std::numeric_limits<double>::digits) << v << "\n";

Это приводит к:

1.000000000000000e-02
1.0000000000000002e-02
1.00000000000000019428902930940239457413554200000000000e-02

На основе @Stephen Canon ответ ниже:

Мы можем точно распечатать, используя форматтер printf (), «% a» или «% A». Для достижения этого в C ++ нам нужно использовать фиксированные и научные манипуляторы (см. N3225: 22.4.2.2.2p5 Таблица 88).

std::cout.flags(std::ios_base::fixed | std::ios_base::scientific);
std::cout << v;

На данный момент я определил:

template<typename T>
std::ostream& precise(std::ostream& stream)
{
    std::cout.flags(std::ios_base::fixed | std::ios_base::scientific);
    return stream;
}

std::ostream& preciselngd(std::ostream& stream){ return precise<long double>(stream);}
std::ostream& precisedbl(std::ostream& stream) { return precise<double>(stream);}
std::ostream& preciseflt(std::ostream& stream) { return precise<float>(stream);}

Далее: Как мы справляемся с NaN / Inf?

 Martin York19 янв. 2011 г., 19:37
@ chrisaycock: нет причин. Вырежьте и вставьте ошибку.
 chrisaycock19 янв. 2011 г., 18:56
Почему вы включаете пробел послеv при выводе наss?
 Thomas Matthews19 янв. 2011 г., 21:30
Всегда будет потеря точности, кроме тех значений, которые имеют двоичный деноменатор. Вопрос должен бытьКакую точность нужно поддерживать?
 Martin York19 янв. 2011 г., 23:28
@ Томас Мэтьюз: я еще не уверен, что это правда. Я не вижу причин, по которым вы не точно храните двойник и извлекаете его. Если у вас есть ссылка, которая показывает это, я хотел бы прочитать это. Это не так, как если бы число с плавающей запятой было нечетким значением, оно имеетточное значение, Может быть, мне нужно распечатать бинарную версию (пока не уверен), но я все еще думаю, что это можно сделать в базе 10.
 Olof Forshell13 авг. 2011 г., 13:45
@ Rüdiger Stevens: причина различий в результатах из-за вычислений в fpu (x86) и sse не в разных стандартах - они оба соответствуют iEEE754 - а потому, что первый хранит промежуточные результаты в 10-байтовом формате с 64 значительными цифры в мантиссе и последней в стандартной двойной точности (иногда одинарной точности) с 1 + 52 (1 + 22) цифрами. Результаты с большим количеством значащих цифр будут лучше обрабатываться fpus, чем sses.

Ответы на вопрос(6)

реобразование в обе стороны - всегда использовать 17 значащих цифр. Но это имеет недостаток, заключающийся в том, что иногда нужно включать ненужные шумовые цифры (0,1 → «0,10000000000000001»).

Подход, который работал для меня, заключается вsprintf число с 15 цифрами точности, затем проверьте, еслиatof возвращает вам первоначальное значение. Если это не так, попробуйте 16 цифр. Еслиэто не работает, используйте 17.

Вы можете попробоватьАлгоритм Дэвида Гея (используется в Python 3.1 для реализацииfloat.__repr__).

 JohnB08 янв. 2016 г., 17:56
Спасибо тебе за это. Это очень хорошая идея.
 Rick Regan01 мая 2016 г., 19:42
В процессе «попробуй 15, затем 16, затем 17» есть интересная аномалия, которая может пропустить 16-значную строку с округлением - см. Мою статьюexploringbinary.com/...

Спасибо ThomasMcLeod за указание на ошибку в вычислении моей таблицы

Гарантировать конвертацию в обе стороны, используя 15 или 16 или 17 цифр, можно только в сравнительно небольшом числе случаев. Число 15,95 происходит от взятия 2 ^ 53 (1 неявный бит + 52 бита в значении и / "мантиссе"), которое получается целым числом в диапазоне от 10 ^ 15 до 10 ^ 16 (ближе к 10 ^ 16).

Рассмотрим значение двойной точности x с показателем 0, т. Е. Оно попадает в диапазон с плавающей запятой 1.0 <= x <2.0. Неявный бит помечает 2 ^ 0 компонент (часть) x. Самый старший явный бит значимого и будет обозначать следующий более низкий показатель (от 0) <=> -1 => 2 ^ -1 или компонент 0,5.

Следующий бит 0,25, после 0,125, 0,0625, 0,03125, 0,015625 и т. Д. (См. Таблицу ниже). Таким образом, значение 1,5 будет представлено двумя сложенными вместе компонентами: неявный бит, обозначающий 1,0, и самый высокий явный бит значени и бит, обозначающий 0,5.

Это показывает, что от неявного бита вниз у вас есть 52 дополнительных, явных бита для представления возможных компонентов, где наименьший равен 0 (экспонента) - 52 (явные биты в значении) = -52 => 2 ^ -52, что в соответствии с таблицей ниже это ... хорошо, вы можете убедиться сами, что получается более 15,95 значащих цифр (37, если быть точным). Другими словами, наименьшее число в диапазоне 2 ^ 0, то есть само! = 1.0, равно 2 ^ 0 + 2 ^ -52, что равно 1.0 + число рядом с 2 ^ -52 (ниже) = (точно) 1.0000000000000002220446049250313080847263336181640625, значение, которое я считаю 53 значащими цифрами. При «точности» форматирования из 17 цифр число будет отображаться как 1.0000000000000002, и это будет зависеть от правильного преобразования библиотеки.

Так что, возможно, «конверсия в 17 цифр туда-обратно» на самом деле не является действительной (достаточной) концепцией.

2^ -1 = 0.5000000000000000000000000000000000000000000000000000
2^ -2 = 0.2500000000000000000000000000000000000000000000000000
2^ -3 = 0.1250000000000000000000000000000000000000000000000000
2^ -4 = 0.0625000000000000000000000000000000000000000000000000
2^ -5 = 0.0312500000000000000000000000000000000000000000000000
2^ -6 = 0.0156250000000000000000000000000000000000000000000000
2^ -7 = 0.0078125000000000000000000000000000000000000000000000
2^ -8 = 0.0039062500000000000000000000000000000000000000000000
2^ -9 = 0.0019531250000000000000000000000000000000000000000000
2^-10 = 0.0009765625000000000000000000000000000000000000000000
2^-11 = 0.0004882812500000000000000000000000000000000000000000
2^-12 = 0.0002441406250000000000000000000000000000000000000000
2^-13 = 0.0001220703125000000000000000000000000000000000000000
2^-14 = 0.0000610351562500000000000000000000000000000000000000
2^-15 = 0.0000305175781250000000000000000000000000000000000000
2^-16 = 0.0000152587890625000000000000000000000000000000000000
2^-17 = 0.0000076293945312500000000000000000000000000000000000
2^-18 = 0.0000038146972656250000000000000000000000000000000000
2^-19 = 0.0000019073486328125000000000000000000000000000000000
2^-20 = 0.0000009536743164062500000000000000000000000000000000
2^-21 = 0.0000004768371582031250000000000000000000000000000000
2^-22 = 0.0000002384185791015625000000000000000000000000000000
2^-23 = 0.0000001192092895507812500000000000000000000000000000
2^-24 = 0.0000000596046447753906250000000000000000000000000000
2^-25 = 0.0000000298023223876953125000000000000000000000000000
2^-26 = 0.0000000149011611938476562500000000000000000000000000
2^-27 = 0.0000000074505805969238281250000000000000000000000000
2^-28 = 0.0000000037252902984619140625000000000000000000000000
2^-29 = 0.0000000018626451492309570312500000000000000000000000
2^-30 = 0.0000000009313225746154785156250000000000000000000000
2^-31 = 0.0000000004656612873077392578125000000000000000000000
2^-32 = 0.0000000002328306436538696289062500000000000000000000
2^-33 = 0.0000000001164153218269348144531250000000000000000000
2^-34 = 0.0000000000582076609134674072265625000000000000000000
2^-35 = 0.0000000000291038304567337036132812500000000000000000
2^-36 = 0.0000000000145519152283668518066406250000000000000000
2^-37 = 0.0000000000072759576141834259033203125000000000000000
2^-38 = 0.0000000000036379788070917129516601562500000000000000
2^-39 = 0.0000000000018189894035458564758300781250000000000000
2^-40 = 0.0000000000009094947017729282379150390625000000000000
2^-41 = 0.0000000000004547473508864641189575195312500000000000
2^-42 = 0.0000000000002273736754432320594787597656250000000000
2^-43 = 0.0000000000001136868377216160297393798828125000000000
2^-44 = 0.0000000000000568434188608080148696899414062500000000
2^-45 = 0.0000000000000284217094304040074348449707031250000000
2^-46 = 0.0000000000000142108547152020037174224853515625000000
2^-47 = 0.0000000000000071054273576010018587112426757812500000
2^-48 = 0.0000000000000035527136788005009293556213378906250000
2^-49 = 0.0000000000000017763568394002504646778106689453125000
2^-50 = 0.0000000000000008881784197001252323389053344726562500
2^-51 = 0.0000000000000004440892098500626161694526672363281250
2^-52 = 0.0000000000000002220446049250313080847263336181640625
 ThomasMcLeod31 янв. 2011 г., 15:43
Во-первых, математика преобразования не верна. Например, 2 ^ -7 - это 0,0078125, а не 0,0070125, как вы опубликовали. Во-вторых, даже если цифры в последней строке были правильными, онине значимы. Они являются базовыми артефактами преобразования. Смотрите мой пост выше.
 Olof Forshell31 янв. 2011 г., 18:50
@ThomasMcLeod: основное правило или базовое предположение? Сколько значащих цифр в пи или е или для GPS? У меня сложилось впечатление, что значительная (ха-ха!) Часть вопросов, связанных с ФП, связана с разными не очень значительными цифрами в конце расчета и стремлением достичь точных или точных результатов, что приводит к разочарованию в отношении того, почему они не были достигнуты Без относительно глубокого погружения в основы FP мы застрянем в ситуациях, когда кто-то спрашивает «сколько цифр», кто-то отвечает «16», а затем «ошибка теперь меньше ... но не исчезла - сработает ли 17?»
 ThomasMcLeod31 янв. 2011 г., 16:42
@ Олоф, как ты определяешь значимое? Если мы разделим 1 на 3, мы получим 0,3333333333333333 ..., но это не значит, что у нас бесконечно значимые цифры. Основное правило: результат математической операции никогда не может иметь более значимых цифр, чем количество значащих цифр любого числового ввода для этой операции.
 Olof Forshell31 янв. 2011 г., 16:27
@ThomasMcLeod: спасибо за указание на ошибки. Что касается вашего утверждения «они не значимы», то я позволю себе не согласиться. В подавляющем большинстве случаев они не будут значительными, но в некоторых они будут. В моем посте была предпринята попытка указать на сложности округления и преобразования, показывая количество задействованных цифр.
 ThomasMcLeod31 янв. 2011 г., 19:25
@Olof, pi и e иррациональны и по определению имеют бесконечно значимые цифры в любой рациональной основе. Однако рациональное представление числа pi и e в двойном рационально, как и любое число с конечным значениемand. Дело в том, что когда вы конвертируете из одной базы в другую, вы не увеличиваете и не можете увеличивать значащие цифры (точность fp) числа. Для этого потребуется увеличение пропускной способности данных фиксированной длины, что невозможно. Я призываю вас найти сценарий, в котором для преобразования туда и обратно требуется более 17 цифр.

95 десятичных разрядов. Видетьhttp://en.wikipedia.org/wiki/IEEE_754-2008, Вам нужно как минимум 16 десятичных цифр, чтобы записать полную точность двойного во всех случаях. [Но см. Четвертое редактирование ниже].

Кстати, это значащие цифры.

Ответ на изменения OP:

Ваше время выполнения с плавающей запятой в десятичную строку выводит намного больше цифр, чем значимо. Дабл может содержать только 52 бита значений (на самом деле 53, если считать «скрытый» 1, который не сохраняется). Это означает, что разрешение не более 2 ^ -53 = 1.11e-16.

Например: 1 + 2 ^ -52 = 1.0000000000000002220446049250313. , , ,

Эти десятичные цифры .0000000000000002220446049250313. , , , самый маленький двоичный «шаг» в двойномпри преобразовании в десятичную.

«Шаг» внутри двойника это:

.0000000000000000000000000000000000000000000000000001в двоичном

Обратите внимание, что двоичный шаг является точным, а десятичный - неточным.

Следовательно, десятичное представление выше,

1.0000000000000002220446049250313. , ,

является неточным представлением точного двоичного числа:

1,0000000000000000000000000000000000000000000000000001.

Третье Править:

Следующее возможное значение для double, которое в точном двоичном виде:

1,0000000000000000000000000000000000000000000000000010

преобразует неточно в десятичном виде

1.0000000000000004440892098500626. , , ,

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

Четвертое Править:

Хотя двойной хранит не более 16 значащих десятичных цифр,иногда для представления числа необходимо 17 десятичных цифр, Причина связана снарезка цифр.

Как я упоминал выше, в двойном числе 52 + 1 двоичных цифр. «+ 1» является предполагаемым ведущим 1 и не является ни сохраненным, ни значимым. В случае целого числа эти 52 двоичных цифры образуют число от 0 до 2 ^ 53 - 1. Сколько десятичных цифр необходимо для хранения такого числа? Ну, log_10 (2 ^ 53 - 1) составляет около 15,95. Таким образом, необходимо не более 16 десятичных цифр. Давайте обозначим эти d_0 до d_15.

Теперь учтите, что числа с плавающей точкой IEEE также имеютдвоичный показатель. Что происходит, когда мы увеличиваем экспонату, скажем, на 2? Мы умножили наше 52-битное число, каким бы оно ни было, на 4. Теперь вместо наших 52 двоичных цифр, идеально совпадающих с нашими десятичными цифрами от d_0 до d_15, мы имеем несколько значимых двоичных цифр, представленных в d_16. Однако, поскольку мы умножили на что-то меньше 10, у нас все еще есть значимые двоичные цифры, представленные в d_0. Таким образом, наши десятичные цифры 15,95 теперь занимают от d_1 до d_15, плюс некоторые старшие биты d_0 и некоторые младшие биты d_16. Вот почему иногда требуется 17 десятичных цифр для представления двойного IEEE.

Пятое Править

Исправлены числовые ошибки

 Martin York19 янв. 2011 г., 20:35
Когда я использую научную запись и точность, это работает именно так, как вы описываете.(numeric_limits<double>::digits10 + 1) == 16, И в моем исходном коде это указывает на отсутствие потери точности. Но когда я печатаю с 53 цифрами, это указывает на то, что точность выше, чем я использовал (см. Выше). Я не понимаю несоответствия.

потому что я пытаюсь (де) сериализовать свои данные в JSON.

Я думаю, что у меня есть более четкое объяснение (с меньшим количеством отказов от руки), почему 17 десятичных цифр достаточно, чтобы восстановить исходное число без потерь:

Представьте себе 3 строки номера:
1. для оригинальной базы 2 номера
2. для округленного основания 10 представлений
3. для восстановленного номера (так же, как # 1, потому что оба в базе 2)

Когда вы конвертируете в базу 10, графически вы выбираете тик на 2-й строке, ближайшей к тику на 1-м. Точно так же, когда вы восстанавливаете оригинал из округленного базового значения 10.

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

Возьмем конкретный случай, когда показатель степени равен 0 для представления base2. Тогда квант base2 будет 2 ^ -52 ~ = 2,22 * 10 ^ -16. Ближайший квант основания 10 меньше 10 ^ -16. Теперь, когда мы знаем требуемый квант основания 10, сколько цифр потребуется для кодирования всех возможных значений? Учитывая, что мы рассматриваем только случай экспоненты = 0, динамический диапазон значений, которые мы должны представить, равен [1.0, 2.0). Следовательно, потребуется 17 цифр (16 цифр для дробной части и 1 цифра для целой части).

Для показателей, отличных от 0, мы можем использовать ту же логику:

    exponent    base2 quant.   base10 quant.  dynamic range   digits needed
    ---------------------------------------------------------------------
    1              2^-51         10^-16         [2, 4)           17
    2              2^-50         10^-16         [4, 8)           17
    3              2^-49         10^-15         [8, 16)          17
    ...
    32             2^-20         10^-7        [2^32, 2^33)       17
    1022          9.98e291      1.0e291    [4.49e307,8.99e307)   17

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

Надеюсь, вам понравится мое объяснение.

 Yale Zhang03 дек. 2014 г., 22:07
Спасибо за поднятые голоса. Я получил некоторое дополнительное понимание. Квант базовых 10 должен быть <= квант базовых 2, поскольку это единственный способ гарантировать, что для каждой точки на числовой линии базовых 2 ближайший тик базовых 10 находится в пределах 1/2 шага! Это гарантирует точное преобразование.
 Martin York24 нояб. 2012 г., 16:46

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

Если у вас есть измерение, которое дает вам значение 1,52, и вы не можете прочитать больше деталей вне шкалы, и скажете, что вы должны добавить другое число (например, другое измерение, потому что эта шкала была слишком мала), скажем, 2 , тогда результат (очевидно) имеет только 2 десятичных знака, то есть 3,52. Но также, если вы добавите 1.1111111111 к значению 1.52, вы получите значение 2.63 (и больше ничего!).

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

Тем не менее, это конкретное правило предназначено только для сложения (для сложения: ошибка результата - это сумма двух ошибок - так что, если вы плохо измеряете только одну, хотя удача, ваша точность ...).

Как получить другие правила: допустим, a - это измеренное число, а δa - ошибка. Допустим, ваша исходная формула была: f: = m a Допустим, вы также измеряете m с ошибкой δm (пусть это будет положительной стороной). Тогда фактический предел: f_up = (m + δm) (a + δa) и f_down = (m-δm) (a-δa) Итак, f_up = m a + δm δa + (δm a + m δa) f_down = m a + δm δa- (δm a + m δa) Следовательно, теперь значащие цифры еще меньше: f_up ~ m a + (δm a + m δa) f_down ~ m a- (δm a + m δa) и, следовательно, δf = δm a + m δa Если вы посмотрите на относительную ошибку, вы получите: δf / f = δm / m + δa / a

Для деления это δf / f = δm / m-δa / a

Надеюсь, что это суть, и надеюсь, что я не сделал слишком много ошибок, уже поздно здесь :-)

tl, dr: значащие цифры означают, сколько цифр на выходе на самом деле происходит от цифр на вашем входе (в реальном мире, а не на искаженном изображении, которое имеют числа с плавающей запятой). Если ваши измерения были 1 с ошибкой «нет» и 3 с ошибкой «нет» и предполагается, что функция равна 1/3, тогда да, все бесконечные цифры являются действительными значащими цифрами. В противном случае обратная операция не будет работать, поэтому, очевидно, они должны быть.

Если правило значащих цифр означает что-то совершенно другое в другой области, продолжайте :-)

Решение Вопроса

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

Вместо этого используйте шестнадцатеричное число с плавающей точкой. В С:

printf("%a\n", yourNumber);

C ++ 0x обеспечиваетhexfloat манипулятор для iostreams, который делает то же самое (на некоторых платформах, используяstd::hex модификатор имеет тот же результат, но это не переносимое предположение).

Использование шестнадцатеричной плавающей запятой предпочтительнее по нескольким причинам.

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

 Martin York19 янв. 2011 г., 23:47
Вы можете получить% форматер, указав фиксированное и научное форматирование.
 ThomasMcLeod19 янв. 2011 г., 23:19
Этот спецификатор типа присутствует во всех средах выполнения? У меня есть в Visual C ++, но некоторые ссылки не имеют его.cplusplus.com/reference/clibrary/cstdio/printf
 Stephen Canon19 янв. 2011 г., 23:21
%a спецификатор был в стандарте C в течение 11 лет; любая платформа, которая все еще не поддерживает ее, не может претендовать на звание «С».hexfloat был добавлен в C ++ 0x (я полагаю - я не парень C ++), поэтому его использование может быть несколько менее переносимым.

Ваш ответ на вопрос