8
Win7 64-bit
gcc 4.8.2
g++ -Wall

I tried to format my C++ sscanf as defined in Why does scanf() need "%lf" for doubles, when printf() is okay with just "%f"? but the return is x == 0. The program is given below. I can't figure out what I did wrong so any advice is welcome.

# include <stdio.h>
# include <ios>
# include <iostream>
# include <iomanip>

#include <cstdlib>

using namespace std;
int main(int argc, char** argv) {
   char buffer[30];
   double x = 5.0;
   sprintf(buffer, "%a", 1.2);
   sscanf(buffer, "%la", &x);
   cout << "    Example 1.2 buffer -> " << buffer << endl;
   cout << "    Example 1.2 scanf <- " << x << endl;

   return 0;
}

output

 Example 1.2 buffer -> 0x1.3333333333333p+0
 Example 1.2 scanf <- 0
Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
lostbits
  • 928
  • 1
  • 9
  • 23
  • Works when I try it on gcc 4.7.2 on Linux, and with clang on OS X. Presume you're aware that `%a` is not standard? The man page does suggest that `l` should not be used with `%a`. – Crowman May 28 '14 at 00:26
  • @PaulGriffiths: `%a` can be found in the C 2011 standard. Section 7.21.6.1, Paragraph 8. – Bill Lynch May 28 '14 at 00:47
  • @sharth: This is a C++ program, though. – Crowman May 28 '14 at 00:49
  • @PaulGriffiths: __C++ 2011 17.6.1.2 Paragraph 4:__ Except as noted in Clauses 18 through 30 and Annex D, the contents of each header `cname` shall be the same as that of the corresponding header `name.h`, as specified in the C standard library or the C Unicode TR, as appropriate, as if by inclusion. – Bill Lynch May 28 '14 at 00:52
  • @sharth: I'm not sure whether `%a` came in the C11 or C99, the latter (along the the TRs) being the one incorporated into C++, but you may be right. Either way, not sure about gcc 4.8.2., but gcc 4.7.2 warns about it, even with `-std=c++11`. – Crowman May 28 '14 at 01:00
  • `sscanf` returns a result indicating how many items were successfully scanned. What value does your `sscanf` call return? (It returns 1 for me.) – Keith Thompson May 28 '14 at 01:09
  • @PaulGriffiths: `"%a"` was introduced by C99. What man page suggests not using `l` with `%a`? `"%la"` is correct. – Keith Thompson May 28 '14 at 01:12
  • 2
    gcc doesn't implement `sscanf`; the runtime library does. If you're using a gcc installation uses the Microsoft library, it might not fully implement `sscanf`; Microsoft tends not to support C99 and later very well. – Keith Thompson May 28 '14 at 01:18
  • @KeithThompson: `man scanf(3)` in the Linux Programmer's Manual. It doesn't come out and say don't do it, but `a` is not one of the listed conversions for `l`. – Crowman May 28 '14 at 01:27
  • @PaulGriffiths: I think that's just an accidental omission. It says farther down that `a` is equivalent to `f`, so `"%la"` should be (and is) equivalent to `"%lf"`. (`"%a"` doesn't require hexadecimal for input.) – Keith Thompson May 28 '14 at 01:32
  • Code could check the result of `sscanf(buffer, "%la", &x);` as in `if (sscanf(buffer, "%la", &x) != 1) HandleError();`. Pedantic code would do `int n; if (sscanf(buffer, "%la%n", &x, &n) != 1 || buffer[n]) HandleError();`. – chux - Reinstate Monica Jun 02 '14 at 03:57

2 Answers2

2

[UPDATE: There's a bug in the Cygwin runtime library "newlib" dealing with hexadecimal floating-point input. As of April 2021, a fix has been submitted but not yet released.]

I see nothing wrong with your code. When I compile and run it on my Linux system, the output is:

    Example 1.2 buffer -> 0x1.3333333333333p+0
    Example 1.2 scanf <- 1.2

The %a specifier for the *printf and *scanf functions was introduced in C99, and incorporated into C++11. For the sprintf call, "%a" is a valid format for an argument of type double, and for the sscanf call "%la" is a valid format for an argument of type double*.

The version of gcc (or g++) is not directly relevant to the problem. gcc is just the compiler; the sscanf function is implemented by the runtime library.

When I compile and run your program under Cygwin on Windows 7, I get the same incorrect output you do. Cygwin uses the "newlib" C library, which differs from the glibc library used on most Linux systems.

If you're using a MinGW installation of gcc, if I recall correctly it uses the Microsoft C library. Microsoft's support for C standards later than 1990 is not good, so it's not too surprising if it doesn't implement sscanf's "%la" format correctly.

Note that you don't need to use "%la" for hexadecimal input. For the *scanf functions, the a, e, f, and g specifiers are all equivalent, and will accept either decimal or hexadecimal floating-point input. So you should be able to use:

sscanf(buffer, "%lf", &x);

But if "%la" doesn't work, chances are "%lf" won't work either.

The sscanf function returns an int result indicating how many items were successfully scanned. You should always check that result. (Though if my experiment on Cygwin is any guide, it won't do any good in this case; sscanf returned 1 but still set x to 0.0.)

Bottom line: Your code is ok, but you're probably using a runtime library that doesn't support what you're trying to do.

Here's another program (it's straight C but it should work as C++) that should give a bit more information. sprintf returns the number of characters written; sscanf returns the number of items read.

#include <stdio.h>

int main(void) {
    char buffer[40];
    char leftover[40];
    double x = 5.0;
    int sprintf_result = sprintf(buffer, "%a", 1.2);
    printf("sprintf returned %d, buffer = \"%s\"\n", sprintf_result, buffer);
    int sscanf_result = sscanf(buffer, "%lf%s", &x, leftover);
    printf("sscanf returned %d, x = %f", sscanf_result, x);
    if (sscanf_result >= 2) {
        printf(", leftover = \"%s\"", leftover);
    }
    putchar('\n');
    return 0;
}

On my Linux system, I get the correct output, which is:

sprintf returned 20, buffer = "0x1.3333333333333p+0"
sscanf returned 1, x = 1.200000

Under Cygwin, I get this output:

sprintf returned 20, buffer = "0x1.3333333333333p+0"
sscanf returned 2, x = 0.000000, leftover = "x1.3333333333333p+0"

which indicates that the "%lf" format caused sscanf to consume just the 0, leaving the rest of the string to be consumed by the "%s". This is characteristic of a C90 implementation (before "%a" was added to the standard) -- but printf's "%a" works correctly.

Try this on your system.

UPDATE 7 years later: I still see the same problem with Cygwin under Windows 10. Further experimentation shows that it doesn't support either the %a format specifier or hexadecimal floating-point input. In C99 and later, then a, e, f, and g specifiers all behave the same way, accepting decimal or hexadecimal input, so for example, a %la format should accept input like 1.0. I've submitted a bug report to the Cygwin mailing list: https://cygwin.com/pipermail/cygwin/2021-April/248315.html

UPDATE 2: A patch has been pushed to newlib-cygwin that should fix this, and new developer snapshots are available. If all goes well, I presume Cygwin will be updated with this fix Real Soon Now.

https://cygwin.com/pipermail/cygwin/2021-April/248323.html

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • )(*)(*&13146)(*^) darn. I use gcc on cygwin. I would like to stay with this and not go to mingw unless I have to (and you mentioned the incompatibility issues w/MSVC libraries). Is there any other choice than to bypass the issue? – lostbits May 28 '14 at 05:43
  • Same here. April 21 2021: confirm that the problem with Cygwin under Windows 10 exists: my test _shows that it doesn't support either the %a format specifier or hexadecimal floating-point input_. – pmor Apr 21 '21 at 09:43
  • BTW, in C it is called `hexadecimal-floating-constant`, in C++ it is called `hexadecimal-floating-literal`. Why there is no consistency in terminology? Confused. – pmor Apr 21 '21 at 09:48
  • @pmor Discussion on the mailing list (see the link at the bottom of this answer) indicates that there's already a patch to fix this: https://cygwin.com/pipermail/cygwin/2021-April/248323.html and a snapshot that you can try. I expect this will be in a Cygwin update Real Soon Now (though of course I can't guarantee anything). – Keith Thompson Apr 21 '21 at 19:08
  • @pmor I'm not sure, but I suspect that Bjarne Stroustrup just liked the word "literal" better than "constant". The terminology changed from to *floating-constant* to *floating-literal* between the 2nd (1991) and 3rd (1997) editions of Stroustrup's "The C++ Programming Language" book. (I find "literal" clearer myself, since it avoids confusion with "constant expression" and "const", but the inconsistency is unfortunate.) – Keith Thompson Apr 21 '21 at 19:19
  • 1
    @KeithThompson Another example: C: _integer constant expression_, C++: _integral constant expression_. – pmor Nov 22 '21 at 20:33
0
sprintf(buffer, "%a", 1.2);

Example 1.2 buffer -> 0x1.3333333333333p+0

The %a format reads the number into hexadecimal floating point format. The number in this format is represented in the form

0xB.CpN

where

  • 0x, prefix
  • B, one hexadecimal digit that represents the leading 1 bit of a normalized binary fraction
  • C, hex digit fractional part
  • N, 2 to the power of N

so:

0x1.3333333333333p+0

is a representation of

1.0011001100110011001100110011001100110011001100110011 x 2^0

This hexadecimal format represents the double-precision floating-point number nearest to the decimal number 1.2.

Regarding why in your example

Example 1.2 scanf <- 0

Compiled with gcc 4.7.2 this works correctly as well as with 4.8.1. The problem is in run-time library you link against.

4pie0
  • 29,204
  • 9
  • 82
  • 118