Несоответствие strtod () и sprintf () в GCC и MSVC - PullRequest
2 голосов
/ 23 марта 2010

Я работаю над кроссплатформенным приложением для Windows и Mac OS X, и у меня возникла проблема с двумя стандартными функциями библиотеки C:

  • strtod() - строка в удвоениепреобразование
  • sprintf() - при использовании для вывода чисел с плавающей запятой двойной точности)

Их версии GCC и MSVC возвращают разные результаты, в некоторых цифрах мантиссы.Но это играет решающую роль, если значение показателя велико.Пример:

MSVC: 9,999999999999999500000000000000e+032
GCC:  9,999999999999999455752309870428e+32
MSVC: 9,999999999999999500000000000000e+033
GCC:  9,999999999999999455752309870428e+33
MSVC: 9,999999999999999700000000000000e+034
GCC:  9,999999999999999686336610791798e+34

Входные тестовые числа имеют идентичное двоичное представление в MSVC и GCC.

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

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

Какие реализации функций строка-двойка и функция двойная-строка вы бы порекомендовали?

Ответы [ 3 ]

2 голосов
/ 23 марта 2010

Преобразование между числами с плавающей запятой и строками сложно - очень сложно.Есть множество работ на эту тему, в том числе:

Последний из них - сокровищница информации о десятичной арифметике с плавающей запятой.

Реализация GNU glibc, вероятно, будет примерно такой же хорошей, как она есть, но она не будет короткой или простой.


Обращаясь к примерам

В двойном случае обычно хранится 16 (некоторые могут утверждать 17) значащих десятичных цифр.MSVC обрабатывает 17 цифр.Все, кроме этого, является шумом.GCC делает, как вы просите, но двоичных разрядов недостаточно, чтобы гарантировать дополнительные 14 цифр, которые вы запрашиваете.Если у вас есть 16-байтовые значения 'long double' (SPARC, PPC, Intel x86_64 для Mac), вы можете получить 32 значащих цифры.Тем не менее, вы видите различия QoI;Я мог бы даже утверждать, что MS выполняет здесь лучшую работу, чем GCC / glibc (и я не часто так говорю!).

0 голосов
/ 09 августа 2011

Следующая функция dtoa возвращает строку, которая без потерь конвертируется обратно в ту же double.

Если переписать aisd для тестирования всех ваших реализаций string -to- float, у вас будет переносимый вывод среди них.

  // Return whether a string represents the given double.
  int aisd(double f, char* s) {
     double r;
     sscanf(s, "%lf", &r);
     return r == f;
  }

  // Return the shortest lossless string representation of an IEEE double.
  // Guaranteed to fit in 23 characters (including the final '\0').
  char* dtoa(char* res, double f) {
     int i, j, lenF = 1e9;
     char fmt[8];
     int e = floor(log10(f)) + 1;

     if (f > DBL_MAX) { sprintf(res, "1e999"); return res; }  // converts to Inf
     if (f < -DBL_MAX) { sprintf(res, "-1e999"); return res; }  // converts to -Inf
     if (isnan(f)) { sprintf(res, "NaN"); return res; }  // NaNs don't work under MSVCRT

     // compute the shortest representation without exponent ("123000", "0.15")
     if (!f || e>-4 && e<21) {
        for (i=0; i<=20; i++) {
           sprintf(fmt, "%%.%dlf", i);
           sprintf(res, fmt, f);
           if (aisd(f, res)) { lenF = strlen(res); break; }
        }
     }

     if (!f) return res;

     // compute the shortest representation with exponent ("123e3", "15e-2")
     for (i=0; i<19; i++) {
        sprintf(res, "%.0lfe%d", f * pow(10,-e), e); if (aisd(f, res)) break;
        j = strlen(res); if (j >= lenF) break;
        while (res[j] != 'e') j--;
        res[j-1]--; if (aisd(f, res)) break;   // try mantissa -1
        res[j-1]+=2; if (aisd(f, res)) break;  // try mantissa +1
        e--;
     }
     if (lenF <= strlen(res)) sprintf(res, fmt, f);
     return res;
  }

См. Не удалось получить NaN из функций MSVCRT strtod / sscanf / atof для проблемы MSVCRT NaN.Если вам не нужно распознавать NaN s, вы можете вывести бесконечность ("1e999"), когда получите его.

0 голосов
/ 09 июля 2010

Единственный известный мне алгоритм печати точного значения числа с плавающей запятой в десятичной форме:

  1. Преобразуйте мантиссу в десятичное целое число. Вы можете сделать это, разделив биты для непосредственного чтения мантиссы, или вы можете написать грязный цикл с плавающей запятой, который сначала умножает значение на степень два, чтобы поместить его в диапазон 1 <= x <10, а затем вытягивает за раз от цифры, приводя к int, вычитая и умножая на 10. </li>
  2. Примените показатель степени путем многократного умножения или деления на 2. Это операция с строкой сгенерированных вами десятичных цифр. Каждые ~ 3 умножения добавят дополнительную цифру слева. Каждое деление добавит дополнительную цифру справа.

Это медленно и некрасиво, но работает ...

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