Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ scanf %la returns 0

Tags:

c++

cygwin

scanf

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
like image 813
lostbits Avatar asked May 28 '14 00:05

lostbits


People also ask

What values does scanf return?

Return Value The scanf() function returns the number of fields that were successfully converted and assigned. The return value does not include fields that were read but not assigned. The return value is EOF for an attempt to read at end-of-file if no conversion was performed.

How does %s work in scanf?

In scanf() you usually pass an array to match a %s specifier, then a pointer to the first element of the array is used in it's place. For other specifiers like %d you need to pass the address of the target variable to allow scanf() to store the result in it.

Does scanf read whitespace?

A white-space character causes the scanf() function to read, but not to store, all consecutive white-space characters in the input up to the next character that is not white space. One white-space character in format-string matches any combination of white-space characters in the input.

What are the parameters of scanf?

Techopedia Explains Scanf%c — Character. %d — Signed integer. %x — Unsigned integer in hexadecimal format. %f — Floating point.


1 Answers

[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

like image 182
Keith Thompson Avatar answered Sep 27 '22 23:09

Keith Thompson