Re[2]: printf double
От: Sergey_BG Россия  
Дата: 14.04.19 07:17
Оценка: 16 (1) +1
Здравствуйте, kov_serg, Вы писали:
_>соответственно "%.16g" (16 значащащих цифр) более чем достаточно.
Это правильно, но... Мы не можем в реальности все числа поместить в 16 знаков, так как первый и последний знак "делят" между собой значимость.
Практически получается, что если первые две цифры более 44, то достаточно 16 знаков, если нет, то можно добавить 17 знак, иначе мы потеряем 1 — 3 бита мантиссы.
Сергей
printf double
От: B0FEE664  
Дата: 10.04.19 12:08
Оценка: :))
Как распечатать double со всей возможной точностью?

#include <stdio.h>      /* printf */
#include <math.h>       /* acos */

int main ()
{
  double pi = 2 * acos (0);
  printf ("The pi = %f\n", pi);
  return 0;
}
И каждый день — без права на ошибку...
Re: printf double
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 13.04.19 08:06
Оценка: 6 (1)
Здравствуйте, B0FEE664, Вы писали:

BFE>Как распечатать double со всей возможной точностью?


Вообще в общем случае рекомендуется %a: хоть оно и 16-ричное, но без конверсии, которая жрёт сколько-то ресурсов, и гарантированно точно.

Ну а если нужно в десятичном — повторюсь — %.17g, %.17e.
Согласно стандартам (я смотрел на C11 final draft), это константа DBL_DECIMAL_DIG.
The God is real, unless declared integer.
Re: printf double
От: watchmaker  
Дата: 10.04.19 13:16
Оценка: 1 (1)
Здравствуйте, B0FEE664, Вы писали:

BFE>Как распечатать double со всей возможной точностью?

Ну конечно так:
printf("The pi = %a\n", pi);

Для a есть требование, что по умолчанию (т.е. если точность не переопределена) должно быть выведено точное представление.
Re[2]: printf double
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 13.04.19 07:53
Оценка: 1 (1)
Здравствуйте, kov_serg, Вы писали:

BFE>>Как распечатать double со всей возможной точностью?

_>Очень просто https://ru.wikipedia.org/wiki/%D0%A7%D0%B8%D1%81%D0%BB%D0%BE_%D0%B4%D0%B2%D0%BE%D0%B9%D0%BD%D0%BE%D0%B9_%D1%82%D0%BE%D1%87%D0%BD%D0%BE%D1%81%D1%82%D0%B8
_>53*ln(2)/ln(10)=15.95
_>соответственно "%.16g" (16 значащащих цифр) более чем достаточно.

Нет, недостаточно. Нужно 17, а не 16. Доказательство:

#include <stdio.h>

int main() {
  double f1 = 0.3;
  double f2 = 0.1 * 3;
  printf("%.16g\n", f1);
  printf("%.16g\n", f2);
  printf("%.17g\n", f1);
  printf("%.17g\n", f2);
}


Запускаем:

$ ./td
0.3
0.3
0.29999999999999999
0.30000000000000004


Вообще, откуда вы взяли 16, совершенно непонятно. По отношению к double, которое IEEE754 binary 64-bit portable float, есть две константы:

1. Сколько цифр десятичного текстового представления сохранится при любом значении десятичного порядка при конверсии в двоичное представление и обратно (берётся самое короткое из десятичных, которое конвертируется в то же двоичное представление), при умолчательном округлении. Это значение равно 15. Константа — DBL_DIG из <float.h>.

2. Сколько цифр десятичного представления нужно, чтобы произвольное число в двоичном виде перевести в десятичное представление и затем из него — в двоичное, чтобы значение сохранилось, при умолчательном округлении. Это число равно 17. Константа — DBL_DECIMAL_DIG из <float.h>.

Для сравнения, для single AKA float AKA IEEE754 binary 32-bit portable float — тут будет 6 и 9 (FLT_DIG, FLT_DECIMAL_DIG соответственно) — их разность на 1 больше. Для single, очень мало значений, что сохранится только 6 цифр, а не 7, но они таки есть, и это одна из причин критики формата и стандарта в целом: сделали бы significand не 23 бита, а 24 — размах порядка бы уменьшился (10^19 вместо 10^38), но точность была бы лучше формализуемой. (Я с этой критикой не согласен, но она достаточно активно звучит.)
The God is real, unless declared integer.
Отредактировано 15.04.2019 6:11 netch80 . Предыдущая версия . Еще …
Отредактировано 13.04.2019 8:08 netch80 . Предыдущая версия .
floating point
Re: printf double
От: LaptevVV Россия  
Дата: 10.04.19 12:30
Оценка: -1
BFE>Как распечатать double со всей возможной точностью?
Прочитай форматы вывода для printf() где-нить в инете...
Там примерно так: %15.14lf — размер.точностьLongFloat
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re: printf double
От: kov_serg Россия  
Дата: 10.04.19 13:14
Оценка: -1
Здравствуйте, B0FEE664, Вы писали:

BFE>Как распечатать double со всей возможной точностью?


Очень просто https://ru.wikipedia.org/wiki/%D0%A7%D0%B8%D1%81%D0%BB%D0%BE_%D0%B4%D0%B2%D0%BE%D0%B9%D0%BD%D0%BE%D0%B9_%D1%82%D0%BE%D1%87%D0%BD%D0%BE%D1%81%D1%82%D0%B8

53*ln(2)/ln(10)=15.95

соответственно "%.16g" (16 значащащих цифр) более чем достаточно.
Re[2]: printf double
От: B0FEE664  
Дата: 10.04.19 12:42
Оценка:
Здравствуйте, LaptevVV, Вы писали:

BFE>>Как распечатать double со всей возможной точностью?

LVV>Прочитай форматы вывода для printf() где-нить в инете...
А где я ? Я не в интернете?

LVV>Там примерно так: %15.14lf — размер.точностьLongFloat

Примерно я и сам могу,
но ведь точность зависит от платформы/компилятора и если просто указать %15.14lf, то где гарантия, что мы не промахнулись с точностью?
И каждый день — без права на ошибку...
Re[3]: printf double
От: reversecode google
Дата: 10.04.19 13:09
Оценка:
вам нечем заняться ?
гугл забит этой фигней
https://stackoverflow.com/questions/16839658/printf-width-specifier-to-maintain-precision-of-floating-point-value
Re[4]: printf double
От: B0FEE664  
Дата: 10.04.19 14:15
Оценка:
Здравствуйте, reversecode, Вы писали:

R>вам нечем заняться ?

R>гугл забит этой фигней
R>https://stackoverflow.com/questions/16839658/printf-width-specifier-to-maintain-precision-of-floating-point-value

Там что-то странное написано.
Начнём с того, что вот это:

#include <float.h>
int Digs = DECIMAL_DIG;
double OneSeventh = 1.0/7.0;
printf("%.*e\n", Digs, OneSeventh);


просто не верно, так как выведет слишком много цифр.

Видимо надо использовать DBL_DECIMAL_DIG, но этот макрос отсутствует для C99 и C++11.
К тому же мне не понятно, надо ли использовать DBL_DECIMAL_DIG или DBL_DECIMAL_DIG + 1.
Так же не понятно, почему точность нельзя подставить прямо в printf. Зачем Digs ?

ЗЫ Если подумать, то видимо надо ещё печатать саму точность, т.е. +/- до следующего/предыдущего представимого числа...
И каждый день — без права на ошибку...
Re: printf double
От: Sergey_BG Россия  
Дата: 10.04.19 16:33
Оценка:
Здравствуйте, B0FEE664, Вы писали:
BFE>Как распечатать double со всей возможной точностью?

Эта задача сводится к задаче: можно ли двоичное число представить в десятичном виде. Ответ да. Но надо помнить, что каждый бит может требовать один десятичный знак для "точной" записи.
например значимость 1-ого бита 0.5. 2-ого бита 0.25 и т.д. 0.125 0.0625.
Т.е. для отображения числа нужно столько же знаков сколько бит в мантисе + в десятичном представлении целой части. Т.е. в худшем случае для числа двойной точности требудется 53 знака + ещё что-то в зависимости от формата и т.п.
Сергей
Отредактировано 10.04.2019 21:28 Sergey_BG . Предыдущая версия .
Re[5]: printf double
От: LaptevVV Россия  
Дата: 12.04.19 11:58
Оценка:
BFE>
BFE>#include <float.h>
BFE>int Digs = DECIMAL_DIG;
BFE>double OneSeventh = 1.0/7.0;
BFE>printf("%.*e\n", Digs, OneSeventh);
BFE>

BFE>просто не верно, так как выведет слишком много цифр.
BFE>Видимо надо использовать DBL_DECIMAL_DIG, но этот макрос отсутствует для C99 и C++11.
BFE>К тому же мне не понятно, надо ли использовать DBL_DECIMAL_DIG или DBL_DECIMAL_DIG + 1.
BFE>Так же не понятно, почему точность нельзя подставить прямо в printf. Зачем Digs ?
Затем, что в формате написана звездочка.
Это чтобы можно было не константно формат задавать.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re[6]: printf double
От: B0FEE664  
Дата: 12.04.19 16:38
Оценка:
Здравствуйте, LaptevVV, Вы писали:

BFE>>
BFE>>#include <float.h>
BFE>>int Digs = DECIMAL_DIG;
BFE>>double OneSeventh = 1.0/7.0;
BFE>>printf("%.*e\n", Digs, OneSeventh);
BFE>>

BFE>>Так же не понятно, почему точность нельзя подставить прямо в printf. Зачем Digs ?
LVV>Затем, что в формате написана звездочка.
LVV>Это чтобы можно было не константно формат задавать.

Почему не так:
printf("%.*e\n", DECIMAL_DIG, OneSeventh);

DECIMAL_DIG — не int?
И каждый день — без права на ошибку...
Re[7]: printf double
От: LaptevVV Россия  
Дата: 13.04.19 03:08
Оценка:
BFE>>>
BFE>>>#include <float.h>
BFE>>>int Digs = DECIMAL_DIG;
BFE>>>double OneSeventh = 1.0/7.0;
BFE>>>printf("%.*e\n", Digs, OneSeventh);
BFE>>>

BFE>Почему не так:
BFE>
BFE>printf("%.*e\n", DECIMAL_DIG, OneSeventh);
BFE>

BFE>DECIMAL_DIG — не int?
Кстати, gcc не ругается и так...
И работает одинаково.
Но не вижу смысла задавать константу в списке переменных.
Смысл именно в том, что меняя ПЕРЕМЕННУЮ Digs — не меняешь код printf()
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re[3]: printf double
От: Sergey_BG Россия  
Дата: 13.04.19 19:45
Оценка:
Здравствуйте, netch80, Вы писали:
N>Нет, недостаточно. Нужно 17, а не 16. Доказательство:
N>
N>  double f1 = 0.3;

Если копать глубже то можно заметить, что 0.3 точно даблом не задать. И изменение последнего бита даёт где-то +-6 в 17 знаке для 0.3. Это можно найти как 0.3*DBL_EPSILON(~2e-016)=~6e-17
Десятичное (17 знаков) — Двоичное (все биты)
0,30000000000000010 — 1.00110011 00110011 00110011 00110011 00110011 00110011 0101 E-10
0,30000000000000004 — 1.00110011 00110011 00110011 00110011 00110011 00110011 0100 E-10
0,29999999999999999 — 1.00110011 00110011 00110011 00110011 00110011 00110011 0011 E-10
0,29999999999999993 — 1.00110011 00110011 00110011 00110011 00110011 00110011 0010 E-10
Сергей
Re[4]: printf double
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 14.04.19 09:10
Оценка:
Здравствуйте, Sergey_BG, Вы писали:

S_B>Здравствуйте, netch80, Вы писали:

N>>Нет, недостаточно. Нужно 17, а не 16. Доказательство:
N>>
N>  double f1 = 0.3;

S_B>Если копать глубже то можно заметить, что 0.3 точно даблом не задать.

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

S_B> И изменение последнего бита даёт где-то +-6 в 17 знаке для 0.3. Это можно найти как 0.3*DBL_EPSILON(~2e-016)=~6e-17

S_B>Десятичное (17 знаков) — Двоичное (все биты)
S_B>0,30000000000000010 — 1.00110011 00110011 00110011 00110011 00110011 00110011 0101 E-10
S_B>0,30000000000000004 — 1.00110011 00110011 00110011 00110011 00110011 00110011 0100 E-10
S_B>0,29999999999999999 — 1.00110011 00110011 00110011 00110011 00110011 00110011 0011 E-10
S_B>0,29999999999999993 — 1.00110011 00110011 00110011 00110011 00110011 00110011 0010 E-10

Имеет смысл показать это также на примере расширенного выхлопа той же тестовой программы: добавляю %a и %.54g:

0x1.3333333333333p-2
0x1.3333333333334p-2
0.3
0.3
0.29999999999999999
0.30000000000000004
0.299999999999999988897769753748434595763683319091796875
0.3000000000000000444089209850062616169452667236328125


В двоичном виде 0.3 и 0.1*3 отличаются младшим битом мантиссы — как у вас и показано, только %a выводит 16рично, а не двоично.

(Интересно, что %.53g недостаточно: насколько я понял описание точности в формате вывода, 1 место занял ведущий ноль.)
The God is real, unless declared integer.
Отредактировано 15.04.2019 6:10 netch80 . Предыдущая версия .
floating point
Re: printf double
От: lis_asm  
Дата: 24.11.19 14:19
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Как распечатать double со всей возможной точностью?


BFE>
BFE>#include <stdio.h>      /* printf */
BFE>#include <math.h>       /* acos */

BFE>int main ()
BFE>{
BFE>  double pi = 2 * acos (0);
BFE>  printf ("The pi = %f\n", pi);
BFE>  return 0;
BFE>}
BFE>




А руками?

typedef long long INTEGER;
typedef double FLOATING;
typedef wchar_t SYMC;


SYMC *PrintNum(INTEGER val, SYMC *str)
{
    if (val < 0)
        *(str++) = '-';

    INTEGER v = val;
    do
        str++;
    while (v /= 10);

    SYMC *strret = str;
    *str = 0;

    do
    {
        int c = val % 10;
        *(--str) = (c < 0 ? -c : c) + '0';
    } while (val /= 10);

    return strret;
}

void PrintFlt(FLOATING val, SYMC *str)
{
    if (val < 0)
    {
        *(str++) = '-';
        val = -val;
    }
    if (val == 0)
    {
        str += StrCpyCvt(str, "0.0");
        *str = 0;
        return;
    }
    FLOATING inf = val * 2;
    if (*(INTEGER *)&val == *(INTEGER *)&inf)
    {
        str += StrCpyCvt(str, "1.#IND");
        *str = 0;
        return;
    }

    int pow = 0;
    FLOATING divser = 1;
    while (val >= divser * 10)
        divser *= 10, pow++;
    while (val < divser)
        divser *= 0.1, pow--;
    int powd = 0;

    int cnt = 14;
    if (pow < 7 && pow > -5)
    {
        powd = pow;
        if (pow < 0)
        {
            cnt -= pow;
            divser = 1;
            powd = 0;
        }
        pow = 0;
    }

    do
    {
        int dig = (int)(val / divser);
        *(str++) = dig + '0';
        if (val == 0 && powd < 0)
            break;
        val -= dig * divser;
        divser *= 0.1;
        if (!(powd--))
            *(str++) = '.';
    } while (cnt--);

    if (pow)
    {   
        *(str++) = 'e';
        str = PrintNum(pow, str);
    }

    *str = 0;
}
Отредактировано 24.11.2019 14:21 lis_asm . Предыдущая версия .
Re[3]: printf double
От: Pzz Россия https://github.com/alexpevzner
Дата: 24.11.19 15:05
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>но ведь точность зависит от платформы/компилятора и если просто указать %15.14lf, то где гарантия, что мы не промахнулись с точностью?


Ну в общем-то, уже не зависит. Сейчас везде IEEE 754
Re[2]: printf double
От: Pzz Россия https://github.com/alexpevzner
Дата: 24.11.19 15:06
Оценка:
Здравствуйте, LaptevVV, Вы писали:

BFE>>Как распечатать double со всей возможной точностью?

LVV>Прочитай форматы вывода для printf() где-нить в инете...
LVV>Там примерно так: %15.14lf — размер.точностьLongFloat

Было бы еще неплохо эту самую точность с реальной точностью внутреннего представления соотнести. А то библиотеке-то не жалко дополнительных циферок в конце понапечатать, но не факт, что они будут что-либо значить...
Re: printf double
От: andyp  
Дата: 24.11.19 20:28
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Как распечатать double со всей возможной точностью?


Если под всей возможной точностью понимается, что потом считанное из вывода число будет внутри те же биты иметь (round trip conversion) и в то же время выводится минимально возможное количество децимальных циферок, то семейство printf — не самый удачный выбор. Надо взять или fmtlib или to_chars из <charconv>. Последний, по крайней мере от микрософт, как хвалятся, это умеет.
Re[2]: printf double
От: B0FEE664  
Дата: 25.11.19 12:15
Оценка:
Здравствуйте, lis_asm, Вы писали:

_>А руками?

_>
_>    int cnt = 14;
_>    if (pow < 7 && pow > -5)
_>


— что за константы?
И каждый день — без права на ошибку...
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.