Какое значение с плавающей запятой заставляет sprintf_s () выдавать «1.# QO»?

#c #visual-studio-2008 #format #floating-point #printf

#c #visual-studio-2008 #формат #значение с плавающей запятой #printf

Вопрос:

У меня есть некоторый (устаревший встроенный c) код, который создает файл .csv с помощью нескольких sprintf вызовов. Иногда я вижу значения 1.#QO . Я пытался воспроизвести эти значения с условиями, которые должны давать отрицательную бесконечность, положительную бесконечность и NaN, но ни одно из них, похоже, не дает мне волшебного 1.#QO результата. Так что же это такое, что выдает это значение?

… и да, я знаю, что, очевидно, что-то идет не так в математике, которая выдает это значение, но понимание того, что это означает, помогло бы в усилиях по отладке.

[Редактировать 1] Фактическая строка, в которой выполняется преобразование, является:

 sprintf_s(txt, CSV_HEADER_SIZE, "%.3f", value);
  

где:

 #define CSV_HEADER_SIZE (100)
char txt[CSV_HEADER_SIZE];
  

Я компилирую с помощью MS Visual Studio 2008.

[Редактировать 2] Еще немного копания показывает, что 0xFFFFFFFF дает -1.#QO :

 unsigned int i = 0xFFFFFFFF;
float* f = (float*)amp;i;
printf("%.3f", *f); // gives -1.#QO
  

.. и, глядя на это в отладчике Visual Studio, расширяет его до -1.#QNAN00 , так что, похоже, это, вероятно, специфичное для Microsoft представление NaN ?

Комментарии:

1. О какой sprintf() строке идет речь?

2. Можете ли вы определить одно из значений, которое дало этот результат, и отформатировать базовые данные с плавающей запятой, скажем, в шестнадцатеричном формате? Например, если это 4-байтовое значение с плавающей запятой, которое вы могли бы printf("%X", value) использовать, или 8-байтовое, которое вы могли бы сделать printf("%llX", value) в зависимости от платформы. Эта информация была бы полезной.

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

4. Я думаю, что 0xFFFFFFFF — это стандартное представление NaN IEEE-754 — однако странный вывод этого числа, похоже, специфичен для Microsoft. Также имеет смысл, что вы получили бы это путем деления на ноль. Здесь обсуждается IEEE 754 NaN: en.wikipedia.org/wiki/NaN

5. также смотрите Примечание в статье Википедии о QNaN (тихий, не подающий сигналов NaN)

Ответ №1:

«-1.#QO» равно «-1.#QNAN» после «округления» на 3 знака после запятой. N округляется до O как ‘A’ >= ‘5’ и ‘N’ 1 == ‘O’.

Аналогично, почему ваш отладчик показывает «-1.#QNAN00», поскольку он печатает с 7 местами и добавляет нули в конце.

QNaN — это тихий NaN.

Комментарии:

1. Я тоже пришел к такому выводу. Странно, что он добавил O в конце, хотя.

2. @JonCage: MSVC использует конечные нули для заполнения требуемых мест, вместо того, чтобы обрабатывать QNAN иначе, чем «обычные» / не-NaN значения. Места после запятой по умолчанию равны 6, поэтому объедините «% f» с NaN (как в вашем ответе) и помните, что strlen («#QNAN») равен 5 … 🙂

3. Этот последний символ — буква ( O ), хотя и не ноль ( 0 ). Я думаю, что это округление N до O , потому что если вы используете, %.2f вы получаете -1.#R 🙂

4. @JonCage: Ах, я пересек сообщения; в вашем ответе у вас есть «-1.#QNAN0». Да, действительно, здесь N округляется до O. Вот почему я заключил округление в кавычки, но я вижу, что это может быть неочевидно. Я расширю ответ.

Ответ №2:

После долгой возни я могу окончательно сказать, что установка 4-байтового значения с плавающей точкой в 0x7FFFFFFF и передача его в sprintf_s со спецификатором формата %.3f — это то, что дало мне 1.#QO :

 const int bufSize = 100;
char buf[bufSize];
unsigned int i;
float* f = (float*)amp;i;
int retval;

i = 0xFFFFFFFF;
retval = sprintf_s(buf, bufSize, "%.3fn", *f);
printf("sprintf_s returned %d, converted val = %s", retval, buf); // == sprintf_s returned 7, converted val = -1.#QO
retval = sprintf_s(buf, bufSize, "%fn", *f);
printf("sprintf_s returned %d, converted val = %s", retval, buf); // == sprintf_s returned 10, converted val = -1.#QNAN0

i = 0x7FFFFFFF;
retval = sprintf_s(buf, bufSize, "%.3fn", *f);
printf("sprintf_s returned %d, converted val = %s", retval, buf); // == sprintf_s returned 6, converted val = 1.#QO
retval = sprintf_s(buf, bufSize, "%fn", *f);
printf("sprintf_s returned %d, converted val = %s", retval, buf); // == sprintf_s returned 9, converted val = 1.#QNAN0
  

… похоже, что %.3f спецификатор формата обрезал результат NAN, так что то, что должно было быть 1.#QNAN0 , было сокращено до 1.#QO .

Ответ №3:

Небольшой поиск в Google указывает на ошибку деления на 0. Хотя я бы ожидал чего-то другого, если бы это было так. Тем не менее, похоже, что это специфично для MS / Visual C.

Комментарии:

1. Конец этого поста в блоге: journal.joshburton.com/2008/01/1qo-value-of-doom.html

Ответ №4:

Вы проверили, вернул ли sprintf_s () ошибку? Если это произойдет, вам не следует использовать результат. Поскольку код не выглядит так, как будто вы проверяли, я думаю, вам следует выполнить эту проверку. На самом деле, если вы не протестируете результат с помощью одной из *_s() функций, у вас будут проблемы.

Комментарии:

1. Из документации, которую я видел [ msdn.microsoft.com/en-us/library/ce3zzk1k(v=vs.80).aspx ] проблема возникает только в том случае, <= 0 если она возвращается, что есть проблема (ошибка или не записаны символы)? Но поскольку значение записывается, по-видимому, нормально, я сомневаюсь, что проблема заключается именно в этом. Фактический код, который мы используем, немного сложнее. Я просто сократил его, чтобы сделать более кратким (возможно, слишком много?). Мы проверяем возвращаемые значения на практике.

2. Если вы вообще тестируете результат, то, вероятно, у вас все в порядке. Из вашего ‘snippet’ не было ясно, что вы проверили возвращаемое значение. Я получил «404 не найдено» по вашей ссылке; но это сработало.

3. Справедливое замечание и стоит проверить, что я не пропустил этот момент 🙂