18

I have some (legacy embedded c) code which produces a .csv file by means of some sprintf calls. Occasionally I see values of 1.#QO. I've tried reproducing those values with conditions which should give negative infinity, positive infinity and NaN but none of them appear to give me the magical 1.#QO result. So what is it that produces that value?

...and yes, I know there's obviously something going wrong in the maths which produce that value, but understanding what it means would assist in the debugging effort.

[Edit 1] The actual line which does the conversion is:

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

where:

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

I'm compiling with MS Visual Studio 2008.

[Edit 2] A bit more digging shows 0xFFFFFFFF gives -1.#QO:

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

..and looking at that in the Visual Studio debugger expands it to -1.#QNAN00 so it looks like this is probably a Microsoft-specific representation of NaN?

mskfisher
  • 3,291
  • 4
  • 35
  • 48
Jon Cage
  • 36,366
  • 38
  • 137
  • 215
  • 3
    What is the `sprintf()` line in question? – CanSpice May 09 '11 at 16:15
  • Can you identify one of the values that produced this result and format out the underlying float data in, say, hex? E.g., if it is a 4-byte float you could `printf("%X", value)`, or for an 8-byte you might be able to do `printf("%llX", value)` depending on the platform. This information would be helpful. – Scott Moonen May 09 '11 at 16:17
  • Could you identify the compiler (and, if applicable, the runtime)? That particular `sprintf` output wasn't in the Standard last I looked, so it's probably very implementation-dependent. – David Thornley May 09 '11 at 16:24
  • I think 0xFFFFFFFF is the IEEE-754 standard representation of NaN--however the strange print out of that number seems Microsoft-specific. Also, makes sense that you'd get this from dividing by zero. IEEE 754 NaN is talked about here: http://en.wikipedia.org/wiki/NaN – nielsbot May 10 '11 at 06:55
  • also, see the note in the wikipedia article about qNaN (quiet, non-signalling NaN) – nielsbot May 10 '11 at 06:56

4 Answers4

11

"-1.#QO" is "-1.#QNAN" after "rounding" for 3 places after the decimal. The N rounds to an O as 'A' >= '5' and 'N' + 1 == 'O'.

This is similarly why your debugger shows "-1.#QNAN00", as it prints with 7 places and adds padding zeros to the end.

QNaN is quiet NaN.

Fred Nurk
  • 13,952
  • 4
  • 37
  • 63
  • That was the conclusion I reached too. Strange that it added the `O` on the end though. – Jon Cage May 10 '11 at 14:57
  • @JonCage: MSVC uses trailing zeros to fill the required places, rather than handle QNAN differently from "regular"/non-NaN values. Default places after the decimal is 6, so combine "%f" with a NaN (as in your answer) and remember strlen("#QNAN") is 5... :) – Fred Nurk May 10 '11 at 15:01
  • 2
    That last character is a letter (`O`) though rather than a zero (`0`). I think it's rounding the `N` up to an `O` because if you use `%.2f` you get `-1.#R` :-) – Jon Cage May 10 '11 at 15:17
  • @JonCage: Ah, I crossed messages; in your answer you have "-1.#QNAN0". Yes, indeed, it "rounds" the N to O here. That's why I put rounding in quotes, but I see it might not be obvious. I'll expand the answer. – Fred Nurk May 10 '11 at 15:33
2

After a lot of fiddling around I can conclusively say that setting a 4-byte float to 0x7FFFFFFF and passing it into sprintf_s with a format specifier of %.3f is what gave me 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

...it seems that the %.3f format specifier was cropping the NAN result so what should have been 1.#QNAN0 was being chopped down to 1.#QO.

Jon Cage
  • 36,366
  • 38
  • 137
  • 215
1

A little googling points to a divide by 0 error. Though I would expect something different if that were the case. That said, it appears to be specific to MS/Visual C.

BMitch
  • 231,797
  • 42
  • 475
  • 450
1

Did you check whether sprintf_s() returned a failure? If it does, you should not use the result. Since the code doesn't look like you checked, I think you should do that checking. In fact, if you don't test the result from one of the *_s() functions, you are headed for trouble.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • From the documentation I've seen [http://msdn.microsoft.com/en-us/library/ce3zzk1k%28v=vs.80%29.aspx] it's only if it's returning `<= 0` that there's a problem (an error or no characters written)? But since the value is being written out seemingly okay, I doubt that's where the problem lies. The actual code we're using is a little more involved. I just chopped it down to make it more concise (perhaps too much?). We do check return values in practice. – Jon Cage May 09 '11 at 22:38
  • If you test the result at all, then you are probably OK. It was not clear from your 'snippet' that you checked the return value. I got '404 not found' from your link; but [this](http://msdn.microsoft.com/en-us/library/ce3zzk1k.aspx) worked. – Jonathan Leffler May 09 '11 at 23:08
  • Fair point and worth checking that I'd not missed that point :-) – Jon Cage May 10 '11 at 13:53