【问题标题】:What float value makes sprintf_s() produce "1.#QO"?什么浮点值使 sprintf_s() 产生“1.#QO”?
【发布时间】:2011-05-09 16:10:34
【问题描述】:

我有一些(遗留的嵌入式 c)代码通过一些 sprintf 调用生成一个 .csv 文件。偶尔我会看到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*)&i;
printf("%.3f", *f); // gives -1.#QO

..在 Visual Studio 调试器中查看它会将其扩展为 -1.#QNAN00,所以看起来这可能是 NaN 的 Microsoft 特定表示?

【问题讨论】:

  • 有问题的sprintf() 行是什么?
  • 你能找出产生这个结果的值之一,并格式化出底层的浮点数据,比如十六进制吗?例如,如果它是 4 字节浮点数,您可以使用 printf("%X", value),或者对于 8 字节浮点数,您可以使用 printf("%llX", value),具体取决于平台。这些信息会很有帮助。
  • 您能否识别编译器(以及,如果适用,运行时)?我上次查看的那个特定的 sprintf 输出不在标准中,所以它可能非常依赖于实现。
  • 我认为 0xFFFFFFFF 是 NaN 的 IEEE-754 标准表示 - 但是该数字的奇怪打印似乎是 Microsoft 特有的。此外,你可以从除以零中得到这个是有道理的。此处讨论 IEEE 754 NaN:en.wikipedia.org/wiki/NaN
  • 另外,请参阅维基百科文章中关于 qNaN(安静、非信号 NaN)的注释

标签: c visual-studio-2008 format floating-point printf


【解决方案1】:

“-1.#QO”在小数点后 3 位“四舍五入”后为“-1.#QNAN”。 N 舍入为 O,因为 'A' >= '5' 和 'N' + 1 == 'O'。

这也是为什么您的调试器显示“-1.#QNAN00”的原因,因为它打印了 7 个位置并在末尾添加了填充零。

QNaN 是 quiet NaN

【讨论】:

  • 这也是我得出的结论。奇怪的是它在末尾添加了O
  • @JonCage:MSVC 使用尾随零来填充所需的位置,而不是处理与“常规”/非 NaN 值不同的 QNAN。小数点后的默认位数是 6,因此将 "%f" 与 NaN 结合起来(如您的答案所示)并记住 strlen("#QNAN") 是 5... :)
  • 最后一个字符是一个字母 (O) 而不是零 (0)。我认为它会将N 舍入到O,因为如果你使用%.2f,你会得到-1.#R :-)
  • @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*)&i;
int retval;

i = 0xFFFFFFFF;
retval = sprintf_s(buf, bufSize, "%.3f\n", *f);
printf("sprintf_s returned %d, converted val = %s", retval, buf); // == sprintf_s returned 7, converted val = -1.#QO
retval = sprintf_s(buf, bufSize, "%f\n", *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, "%.3f\n", *f);
printf("sprintf_s returned %d, converted val = %s", retval, buf); // == sprintf_s returned 6, converted val = 1.#QO
retval = sprintf_s(buf, bufSize, "%f\n", *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】:

    一点谷歌搜索指出除以 0 错误。虽然如果是这样的话,我会期待一些不同的东西。也就是说,它似乎是特定于 MS/Visual C 的。

    【讨论】:

    【解决方案4】:

    您是否检查过 sprintf_s() 是否返回失败?如果是这样,则不应使用结果。由于代码看起来不像您检查过的,我认为您应该进行检查。事实上,如果你不测试 *_s() 函数之一的结果,你就会遇到麻烦。

    【讨论】:

    • 从我看到的文档 [msdn.microsoft.com/en-us/library/ce3zzk1k%28v=vs.80%29.aspx] 中,只有当它返回 <= 0 时才会出现问题(错误或没有写入字符)?但是由于该值被写出来似乎没问题,我怀疑这就是问题所在。我们使用的实际代码更复杂一些。我只是把它砍下来让它更简洁(也许太多了?)。我们会在实践中检查返回值。
    • 如果您完全测试结果,那么您可能没问题。从您的“sn-p”中不清楚您检查了返回值。我从您的链接中得到“404 not found”;但this 工作。
    • 公平点,值得检查一下我没有错过这一点:-)
    猜你喜欢
    • 1970-01-01
    • 2016-07-13
    • 2014-07-23
    • 1970-01-01
    • 2023-03-16
    • 1970-01-01
    • 1970-01-01
    • 2016-06-30
    • 1970-01-01
    相关资源
    最近更新 更多