Спецификатор ширины Printf для поддержания точности значения с плавающей запятой

103

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

Например, предположим, что я печатаю floatс точностью до 2десятичных знаков:

float foobar = 0.9375;
printf("%.2f", foobar);    // prints out 0.94

Когда я просматриваю вывод 0.94, у меня нет соответствующей стандартам гарантии, что я верну исходное значение с 0.9375плавающей запятой (в этом примере я, вероятно, не буду).

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

Я мог бы использовать некоторые из макросов, float.hчтобы получить максимальную ширину для перехода printf, но есть ли уже спецификатор для автоматической печати до необходимого количества значащих цифр - или, по крайней мере, до максимальной ширины?

Вильгельм Грей
источник
4
@bobobobo Итак, вы просто рекомендуете использовать предположение из воздуха вместо того, чтобы использовать переносимый подход?
1
@ H2CO3 Нет, я бы не рекомендовал использовать «предположения из воздуха», я бы предложил использовать то, printf( "%f", val );что уже переносимо, эффективно и по умолчанию.
bobobobo
2
@bobobobo Чтобы я мог добавить его к ответам, не могли бы вы процитировать пункт стандарта C99, в котором говорится, что оператор printf будет выводить тип float с максимальной точностью по умолчанию, если точность не указана?
Вильгельм Грей
1
@VilhelmGray Ну, поскольку @chux вникает, есть довольно сложная математика относительно фактической точности для вашего конкретного double. Поскольку ваш doubleстановится очень большим (очень далеко от 1.0), он становится менее точным в десятичной части (часть значения меньше 1.0). Итак, у вас не может быть здесь удовлетворительного ответа, потому что ваш вопрос содержит ложное предположение (а именно, что все floatс / doubleс созданы равными)
bobobobo
2
@Vilhelm Gray C11dr 5.2.4.2.2 "... количество десятичных цифр, n, такое, что любое число с плавающей запятой с p основанием b цифр может быть округлено до числа с плавающей запятой с n десятичными цифрами и обратно без изменений к значению, p log10 bb - это степень 10 ⎡1 + p log10 b⎤, иначе FLT_DECIMAL_DIG 6 DBL_DECIMAL_DIG 10 LDBL_DECIMAL_DIG 10 ... «6,10,10 являются минимальными значениями.
chux

Ответы:

92

Я рекомендую шестнадцатеричное решение @Jens Gustedt: используйте% a.

OP хочет «печатать с максимальной точностью (или, по крайней мере, до самого старшего десятичного числа)».

Простой пример - напечатать одну седьмую, как в:

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

Но давайте копнем глубже ...

Математически ответ будет «0,142857 142857 142857 ...», но мы используем числа с плавающей запятой конечной точности. Предположим, IEEE 754 двоичный код двойной точности . Итак, OneSeventh = 1.0/7.0результаты в значении ниже. Также показаны предыдущие и последующие представимые doubleчисла с плавающей запятой.

OneSeventh before = 0.1428571428571428 214571170656199683435261249542236328125
OneSeventh        = 0.1428571428571428 49212692681248881854116916656494140625
OneSeventh after  = 0.1428571428571428 769682682968777953647077083587646484375

Печать точного десятичного представления a doubleимеет ограниченное применение.

В C есть 2 семейства макросов, которые <float.h>могут нам помочь.
Первый набор - это количество значащих цифр для печати в строке в десятичном формате, поэтому при обратном сканировании строки мы получаем исходную плавающую точку. Они показаны с минимальным значением спецификации C и образцом компилятора C11.

FLT_DECIMAL_DIG   6,  9 (float)                           (C11)
DBL_DECIMAL_DIG  10, 17 (double)                          (C11)
LDBL_DECIMAL_DIG 10, 21 (long double)                     (C11)
DECIMAL_DIG      10, 21 (widest supported floating type)  (C99)

Второй набор - это количество значащих цифр, которые строка может быть отсканирована в число с плавающей запятой, а затем напечатана FP, все еще сохраняя то же представление строки. Они показаны с минимальным значением спецификации C и образцом компилятора C11. Я считаю доступным до C99.

FLT_DIG   6, 6 (float)
DBL_DIG  10, 15 (double)
LDBL_DIG 10, 18 (long double)

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

#ifdef DBL_DECIMAL_DIG
  #define OP_DBL_Digs (DBL_DECIMAL_DIG)
#else  
  #ifdef DECIMAL_DIG
    #define OP_DBL_Digs (DECIMAL_DIG)
  #else  
    #define OP_DBL_Digs (DBL_DIG + 3)
  #endif
#endif

«+3» было сутью моего предыдущего ответа. Он сосредоточен на том, зная, что строка-FP-строка двустороннего преобразования (набор макросов № 2, доступных C89), как можно определить цифры для FP-string-FP (установить макрос № 1, доступный после C89)? В общем, результат прибавил 3.

Теперь известно, сколько значащих цифр нужно напечатать <float.h>.

Для вывода N значащих десятичных цифр можно использовать различные форматы.

С "%e", то точность поле количество цифр после свинцовой цифр и десятичной точки. Так - 1что по порядку. Примечание: этого -1нет в начальномint Digs = DECIMAL_DIG;

printf("%.*e\n", OP_DBL_Digs - 1, OneSeventh);
// 1.4285714285714285e-01

С "%f", то точность поле количество цифр после десятичной точки. Для такого числа OneSeventh/1000000.0нужно OP_DBL_Digs + 6видеть все значащие цифры.

printf("%.*f\n", OP_DBL_Digs    , OneSeventh);
// 0.14285714285714285
printf("%.*f\n", OP_DBL_Digs + 6, OneSeventh/1000000.0);
// 0.00000014285714285714285

Примечание: многие используются "%f". Это отображает 6 цифр после десятичной точки; По умолчанию отображается 6, а не точность числа.

chux - восстановить Монику
источник
почему 1.428571428571428492127e-01, а не 1.428571428571428492127e-0 0 1, количество цифр после 'e' должно быть 3?
user1024
12.12.5 «Преобразования с плавающей запятой» говорят, что точность по умолчанию %f- 6.
Цзинго Яо,
1
@Jingguo Yao Согласитесь, что ссылка гласит: «Точность определяет, сколько цифр следует за десятичной точкой для '% f'». Слово «точность» здесь не используется в математическом смысле, а просто для определения количества цифр после десятичной точки. 1234567890.123, математически имеет 13 знаков точности или значащих цифр. 0.000000000123 имеет 3 цифры математической точности, а не 13. Числа с плавающей запятой распределены логарифмически. В этом ответе используются значащие цифры и математическое чувство точности .
chux
1
@Slipp D. Thompson "Показаны с минимальным значением спецификации C и образцом компилятора C11."
chux
1
Действительно, вы правы - мой трюк действителен только для значений с величиной от 1.0 до 1.0eDBL_DIG, что, возможно, является единственным диапазоном, действительно подходящим для печати "%f"в первую очередь. Использование, "%e"как вы показали, конечно, лучший подход во всех отношениях и, по сути, достойный ответ (хотя, возможно, это не так хорошо, как использование "%a"могло бы быть, если оно доступно, и, конечно, "%a"должно быть доступно, если есть `DBL_DECIMAL_DIG). Мне всегда хотелось иметь спецификатор формата, который всегда округлял бы точно до максимальной точности (вместо жестко запрограммированных 6 десятичных знаков).
Грег А. Вудс,
66

Краткий ответ на печать чисел с плавающей запятой без потерь (таким образом, чтобы их можно было прочитать обратно с точно таким же числом, кроме NaN и Infinity):

  • Если ваш тип - float: используйте printf("%.9g", number).
  • Если ваш тип двойной: используйте printf("%.17g", number).

НЕ используйте %f, так как это только указывает, сколько значащих цифр после десятичной дроби, и будет усекать маленькие числа. Для справки можно найти магические числа 9 и 17, float.hкоторые определяют FLT_DECIMAL_DIGи DBL_DECIMAL_DIG.

ccxvii
источник
6
Сможете ли вы объяснить %gспецификатор?
Вильгельм Грей
14
% g печатает число с таким количеством цифр, которое необходимо для точности, предпочитая экспоненциальный синтаксис, когда числа маленькие или огромные (1e-5, а не .00005), и пропускает любые конечные нули (1 вместо 1.00000).
ccxvii
4
@truthseeker Для представления кода IEEE 754 binary64 действительно необходимо вывести не менее 15 значащих десятичных знаков. Но для однозначности требуется 17, поскольку точность изменяется в двоичном числе (2,4,8 и т. Д.), А десятичное число (в 10,100,1000 и т. Д.) Никогда не совпадают (кроме 1,0). Пример: 2 doubleзначения чуть выше 0.1: 1.000_0000_0000_0000_2e-01, 1.000_0000_0000_0000_3e-01потребность 17 цифр , чтобы отличить.
chux
3
@chux - Вы ошибаетесь насчет поведения% .16g; это не подходит для вашего примера различения 1.000_0000_0000_0000_2e-01 от 1.000_0000_0000_0000_3e-01. % .17g не требуется.
Дон Хэтч
1
@Don Hatch Я согласен "%.16g"недостаточно , и "%.17g"и "%.16e"являются достаточными. Детали %g, которые я забыл.
chux
23

Если вас интересует только бит (или шестнадцатеричный шаблон), вы можете использовать %aформат. Это гарантирует вам:

Точности по умолчанию достаточно для точного представления значения, если существует точное представление в базе 2, а в противном случае она достаточно велика, чтобы различать значения типа double.

Я должен добавить, что это доступно только с C99.

Йенс Густедт
источник
16

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

Максимальная точность floatи doubleявляется переменной и зависит от фактического значения из floatили double.

Напомним floatи doubleхранятся в формате sign.exponent.mantissa . Это означает, что для дробной составляющей малых чисел используется намного больше битов, чем для больших чисел.

введите описание изображения здесь

Например, floatможно легко различить 0,0 и 0,1.

float r = 0;
printf( "%.6f\n", r ) ; // 0.000000
r+=0.1 ;
printf( "%.6f\n", r ) ; // 0.100000

Но floatпонятия не имеет о разнице между 1e27и 1e27 + 0.1.

r = 1e27;
printf( "%.6f\n", r ) ; // 999999988484154753734934528.000000
r+=0.1 ;
printf( "%.6f\n", r ) ; // still 999999988484154753734934528.000000

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

%.fМодификатор просто говорит , сколько десятичных значений вы хотите напечатать из числа поплавка, насколько форматирование идет. Тот факт, что доступная точность зависит от размера числа , зависит от вас, как от программиста . printfне может / не справляется с этим за вас.

бобобобо
источник
2
Это отличное объяснение ограничений точной печати значений с плавающей запятой до определенных десятичных знаков. Однако я считаю, что я был слишком двусмыслен в своем первоначальном выборе слов, поэтому я обновил свой вопрос, чтобы избежать термина «максимальная точность» в надежде, что это может устранить путаницу.
Вильгельм Грей
Это все еще зависит от значения печатаемого числа.
bobobobo
3
отчасти это правда, но он не отвечает на вопрос, и вы не понимаете, что спрашивает OP. Он спрашивает, можно ли запросить количество значащих [десятичных] цифр, которые floatпредоставляет a , и вы утверждаете, что такого нет (то есть, что нет FLT_DIG), что неверно.
@ H2CO3 Может быть, вам стоит отредактировать мой пост и проголосовать против (j / k). Этот ответ утверждает, что FLT_DIGничего не значит. Этот ответ утверждает, что количество доступных десятичных знаков зависит от значения внутри числа с плавающей запятой .
bobobobo
1
Вы предполагаете, что форматная буква должна быть "f"? Я не думаю, что это необходимо. Я читаю вопрос о том, что OP ищет некоторый спецификатор формата printf, который производит двусторонний обход без потерь, поэтому ответ @ccxvii ("% .9g" для float, "% .17g" для double) является хороший. Наверное, вопрос лучше сформулировать, убрав из него слово «ширина».
Дон Хэтч
11

Просто используйте макросы from <float.h>и спецификатор преобразования переменной ширины ( ".*"):

float f = 3.14159265358979323846;
printf("%.*f\n", FLT_DIG, f);
бобобобо
источник
2
@OliCharlesworth. Ты имеешь в виду вот так:printf("%." FLT_DIG "f\n", f);
Вильгельм Грей
3
+1, но это работает лучше всего %e, а не для %f: только если известно, что значение для печати близко к 1.0.
Паскаль Куок
3
%eпечатает значащие цифры для очень маленьких чисел и %fне печатает . напр x = 1e-100. %.5fотпечатки 0.00000(полная потеря прецессии). %.5eпечатает 1.00000e-100.
chux - Восстановить Монику
1
@bobobobo Кроме того, вы ошибаетесь в том, что это "дает более точные причины". FLT_DIGопределяется значением, для которого он определен по какой-то причине. Если это 6, это потому, что floatне может содержать более 6 цифр точности. Если распечатать его с помощью %.7f, последняя цифра не будет иметь значения. Подумайте, прежде чем голосовать против.
5
@bobobobo Нет, %.6fне эквивалент, потому что FLT_DIGне всегда 6. А кого волнует эффективность? Ввод-вывод уже чертовски дорог, точность до одной цифры не станет узким местом.
5

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

#include <float.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

union {
    short s[4];
    double d;
} u;

void
test(int digits)
{
    int i, j;
    char buff[40];
    double d2;
    int n, num_equal, bin_equal;

    srand(17);
    n = num_equal = bin_equal = 0;
    for (i = 0; i < 1000000; i++) {
        for (j = 0; j < 4; j++)
            u.s[j] = (rand() << 8) ^ rand();
        if (isnan(u.d))
            continue;
        n++;
        sprintf(buff, "%.*g", digits, u.d);
        sscanf(buff, "%lg", &d2);
        if (u.d == d2)
            num_equal++;
        if (memcmp(&u.d, &d2, sizeof(double)) == 0)
            bin_equal++;
    }
    printf("Tested %d values with %d digits: %d found numericaly equal, %d found binary equal\n", n, digits, num_equal, bin_equal);
}

int
main()
{
    test(DBL_DECIMAL_DIG);
    test(DBL_DECIMAL_DIG - 1);
    return 0;
}

Я запускаю это с компилятором Microsoft C 19.00.24215.1 и gcc версии 7.4.0 20170516 (Debian 6.3.0-18 + deb9u1). Использование одной десятичной цифры уменьшает вдвое количество сравниваемых чисел, которые точно равны. (Я также подтвердил, что rand()при использовании действительно получается около миллиона различных чисел.) Вот подробные результаты.

Microsoft C

Протестировано 999507 значений с 17 цифрами: 999507 найдено числовое равенство, 999507 найдено двоичное равенство
Протестировано 999507 значений с 16 цифрами: 545389 найдено числовое равенство, 545389 найдено двоичное равенство

GCC

Протестировано 999485 значений с 17 цифрами: 999485 найдено числовое равенство, 999485 найдено двоичное равенство
Протестировано 999485 значений с 16 цифрами: 545402 найдено числовое равенство, 545402 найдено двоичное равенство
Диомидис Спинеллис
источник
1
"запустите это с помощью компилятора Microsoft C" -> Этот компилятор может иметь RAND_MAX == 32767. Подумайте u.s[j] = (rand() << 8) ^ rand();или что-то подобное, чтобы убедиться, что все биты имеют шанс быть 0 или 1.
chux - Восстановить Монику
Действительно, его RAND_MAX - 32767, поэтому ваше предложение правильное.
Диомидис Спинеллис,
1
Я обновил сообщение для обработки RAND_MAX, как это было предложено @ chux-ReinstateMonica. Результаты аналогичны полученным ранее.
Диомидис Спинеллис,
3

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

static unsigned int
ilog10(uintmax_t v);

/*
 * Note:  As presented this demo code prints a whole line including information
 * about how the form was arrived with, as well as in certain cases a couple of
 * interesting details about the number, such as the number of decimal places,
 * and possibley the magnitude of the value and the number of significant
 * digits.
 */
void
print_decimal(double d)
{
        size_t sigdig;
        int dplaces;
        double flintmax;

        /*
         * If we really want to see a plain decimal presentation with all of
         * the possible significant digits of precision for a floating point
         * number, then we must calculate the correct number of decimal places
         * to show with "%.*f" as follows.
         *
         * This is in lieu of always using either full on scientific notation
         * with "%e" (where the presentation is always in decimal format so we
         * can directly print the maximum number of significant digits
         * supported by the representation, taking into acount the one digit
         * represented by by the leading digit)
         *
         *        printf("%1.*e", DBL_DECIMAL_DIG - 1, d)
         *
         * or using the built-in human-friendly formatting with "%g" (where a
         * '*' parameter is used as the number of significant digits to print
         * and so we can just print exactly the maximum number supported by the
         * representation)
         *
         *         printf("%.*g", DBL_DECIMAL_DIG, d)
         *
         *
         * N.B.:  If we want the printed result to again survive a round-trip
         * conversion to binary and back, and to be rounded to a human-friendly
         * number, then we can only print DBL_DIG significant digits (instead
         * of the larger DBL_DECIMAL_DIG digits).
         *
         * Note:  "flintmax" here refers to the largest consecutive integer
         * that can be safely stored in a floating point variable without
         * losing precision.
         */
#ifdef PRINT_ROUND_TRIP_SAFE
# ifdef DBL_DIG
        sigdig = DBL_DIG;
# else
        sigdig = ilog10(uipow(FLT_RADIX, DBL_MANT_DIG - 1));
# endif
#else
# ifdef DBL_DECIMAL_DIG
        sigdig = DBL_DECIMAL_DIG;
# else
        sigdig = (size_t) lrint(ceil(DBL_MANT_DIG * log10((double) FLT_RADIX))) + 1;
# endif
#endif
        flintmax = pow((double) FLT_RADIX, (double) DBL_MANT_DIG); /* xxx use uipow() */
        if (d == 0.0) {
                printf("z = %.*s\n", (int) sigdig + 1, "0.000000000000000000000"); /* 21 */
        } else if (fabs(d) >= 0.1 &&
                   fabs(d) <= flintmax) {
                dplaces = (int) (sigdig - (size_t) lrint(ceil(log10(ceil(fabs(d))))));
                if (dplaces < 0) {
                        /* XXX this is likely never less than -1 */
                        /*
                         * XXX the last digit is not significant!!! XXX
                         *
                         * This should also be printed with sprintf() and edited...
                         */
                        printf("R = %.0f [%d too many significant digits!!!, zero decimal places]\n", d, abs(dplaces));
                } else if (dplaces == 0) {
                        /*
                         * The decimal fraction here is not significant and
                         * should always be zero  (XXX I've never seen this)
                         */
                        printf("R = %.0f [zero decimal places]\n", d);
                } else {
                        if (fabs(d) == 1.0) {
                                /*
                                 * This is a special case where the calculation
                                 * is off by one because log10(1.0) is 0, but
                                 * we still have the leading '1' whole digit to
                                 * count as a significant digit.
                                 */
#if 0
                                printf("ceil(1.0) = %f, log10(ceil(1.0)) = %f, ceil(log10(ceil(1.0))) = %f\n",
                                       ceil(fabs(d)), log10(ceil(fabs(d))), ceil(log10(ceil(fabs(d)))));
#endif
                                dplaces--;
                        }
                        /* this is really the "useful" range of %f */
                        printf("r = %.*f [%d decimal places]\n", dplaces, d, dplaces);
                }
        } else {
                if (fabs(d) < 1.0) {
                        int lz;

                        lz = abs((int) lrint(floor(log10(fabs(d)))));
                        /* i.e. add # of leading zeros to the precision */
                        dplaces = (int) sigdig - 1 + lz;
                        printf("f = %.*f [%d decimal places]\n", dplaces, d, dplaces);
                } else {                /* d > flintmax */
                        size_t n;
                        size_t i;
                        char *df;

                        /*
                         * hmmmm...  the easy way to suppress the "invalid",
                         * i.e. non-significant digits is to do a string
                         * replacement of all dgits after the first
                         * DBL_DECIMAL_DIG to convert them to zeros, and to
                         * round the least significant digit.
                         */
                        df = malloc((size_t) 1);
                        n = (size_t) snprintf(df, (size_t) 1, "%.1f", d);
                        n++;                /* for the NUL */
                        df = realloc(df, n);
                        (void) snprintf(df, n, "%.1f", d);
                        if ((n - 2) > sigdig) {
                                /*
                                 * XXX rounding the integer part here is "hard"
                                 * -- we would have to convert the digits up to
                                 * this point back into a binary format and
                                 * round that value appropriately in order to
                                 * do it correctly.
                                 */
                                if (df[sigdig] >= '5' && df[sigdig] <= '9') {
                                        if (df[sigdig - 1] == '9') {
                                                /*
                                                 * xxx fixing this is left as
                                                 * an exercise to the reader!
                                                 */
                                                printf("F = *** failed to round integer part at the least significant digit!!! ***\n");
                                                free(df);
                                                return;
                                        } else {
                                                df[sigdig - 1]++;
                                        }
                                }
                                for (i = sigdig; df[i] != '.'; i++) {
                                        df[i] = '0';
                                }
                        } else {
                                i = n - 1; /* less the NUL */
                                if (isnan(d) || isinf(d)) {
                                        sigdig = 0; /* "nan" or "inf" */
                                }
                        }
                        printf("F = %.*s. [0 decimal places, %lu digits, %lu digits significant]\n",
                               (int) i, df, (unsigned long int) i, (unsigned long int) sigdig);
                        free(df);
                }
        }

        return;
}


static unsigned int
msb(uintmax_t v)
{
        unsigned int mb = 0;

        while (v >>= 1) { /* unroll for more speed...  (see ilog2()) */
                mb++;
        }

        return mb;
}

static unsigned int
ilog10(uintmax_t v)
{
        unsigned int r;
        static unsigned long long int const PowersOf10[] =
                { 1LLU, 10LLU, 100LLU, 1000LLU, 10000LLU, 100000LLU, 1000000LLU,
                  10000000LLU, 100000000LLU, 1000000000LLU, 10000000000LLU,
                  100000000000LLU, 1000000000000LLU, 10000000000000LLU,
                  100000000000000LLU, 1000000000000000LLU, 10000000000000000LLU,
                  100000000000000000LLU, 1000000000000000000LLU,
                  10000000000000000000LLU };

        if (!v) {
                return ~0U;
        }
        /*
         * By the relationship "log10(v) = log2(v) / log2(10)", we need to
         * multiply "log2(v)" by "1 / log2(10)", which is approximately
         * 1233/4096, or (1233, followed by a right shift of 12).
         *
         * Finally, since the result is only an approximation that may be off
         * by one, the exact value is found by subtracting "v < PowersOf10[r]"
         * from the result.
         */
        r = ((msb(v) * 1233) >> 12) + 1;

        return r - (v < PowersOf10[r]);
}
Грег А. Вудс
источник
Меня не волнует, ответит он на вопрос или нет - это действительно впечатляет. Это потребовало некоторого размышления, и его следует признать и похвалить. Может быть, было бы хорошо, если бы вы каким-то образом (здесь или иначе) включили полный код для тестирования, но даже без этого это действительно хорошая работа. Получите за это +1!
Pryftan
0

Насколько мне известно, есть хорошо диффундирует алгоритм , позволяющий вывод на необходимое количество значащих цифр , например , что при сканировании строки обратно, исходное значение с плавающей точкой приобретаются в dtoa.cнаписанной Daniel Gay, который доступен здесь на Netlib (см также соответствующий документ ). Этот код используется, например, в Python, MySQL, Scilab и многих других.

Стефан Моттле
источник