sscanf(“0X80.9”, “%f”, &value) sets value to a hex float not a float

穿精又带淫゛_ 提交于 2019-12-11 06:43:28

问题


I have some code that reads letter value pairs. It can be simplified as:

float value = 0.0;
sscanf("0X80.9", "%f", &value);

In Visual Studio 2013, value = 0

In Visual Studio 2015 and above, value = 128.5625 (i.e. 0x80 in hex = 128 in decimal)

Anyone know why this has changed? More importantly, anyone know how to make it work like it used to?


回答1:


C99 added a new feature, hexadecimal floating-point constants. It also added new formats to the *scanf() functions. (A hexadecimal floating-point constant in source code must have an exponent part; for scanf, the exponent is optional.)

scanf reads as much of the input as it can that's consistent with the format. For pre-1999 C, this is the initial "0"; the following "X80.9"is left. For C99, "0X80.9" is valid, and is converted to the floating-point value 128.5625. (The format is the same as what's accepted by strtof().)

The C++ standard includes most of the C standard library by reference. It didn't update to the C99 version of the C library until c++2012.

Most likely Visual Studio 2015 supports C++12 or later, and Visual Studio 2013 doesn't.

Since the new behavior is now standard, I suggest adapting your code to deal with it rather than trying to force the old behavior. If there's a way to make VS2015 behave in the old way, it should be documented.




回答2:


C99 adds hexadecimal floating point constants. The printf() formats are %a and %A; there are the corresponding scanf() formats too. But as with %f, %e, %g, the scanf() floating point formats all accept any floating point format (so %f can read output printed using %e, etc).

ISO/IEC 9899:2011 — §6.4.4.2 Floating constants

floating-constant:
        decimal-floating-constant
        hexadecimal-floating-constant

hexadecimal-floating-constant:
        hexadecimal-prefix hexadecimal-fractional-constant binary-exponent-part floating-suffixopt
        hexadecimal-prefix hexadecimal-digit-sequence binary-exponent-part floating-suffixopt

hexadecimal-fractional-constant:
        hexadecimal-digit-sequenceopt . hexadecimal-digit-sequence
        hexadecimal-digit-sequence .

binary-exponent-part:
        p signopt digit-sequence
        P signopt digit-sequence

hexadecimal-digit-sequence:
        hexadecimal-digit
        hexadecimal-digit-sequence hexadecimal-digit

floating-suffix: one of
        f l F L

(The hexadecimal prefix is 0x or 0X of course.)

The 0X80.9 notation doesn't exactly correspond to the notation required of hexadecimal floating point constants in C source code because there is no 'binary-exponent-part', but …

  1. the scanf() family of functions can only push back 1 character, so they can't tell there's a problem until far too late, so it ends up treating it as if there was a P0 at the end. The scanf() functions read data items according to a conversion specification:

    An input item is read from the stream, unless the specification includes an n specifier. An input item is defined as the longest sequence of input characters which does not exceed any specified field width and which is, or is a prefix of, a matching input sequence.285) The first character, if any, after the input item remains unread.

  2. the strtod() family of functions accept:

    — a 0x or 0X, then a nonempty sequence of hexadecimal digits optionally containing a decimal-point character, then an optional binary exponent part as defined in 6.4.4.2;

  3. and the scanf() family of functions accept:

    a, e, f, g
    Matches an optionally signed floating-point number, infinity, or NaN, whose format is the same as expected for the subject sequence of the strtod function. The corresponding argument shall be a pointer to floating.




回答3:


In the 1999 C Standard it was added that the input for strtod (which is what %f in scanf refers to) can include a 0x or 0X prefix to indicate a hex float.

16 years later it seems Microsoft have finally gotten around to doing the work.

Your best option would be to move forward with the new behaviour, rather than trying to invoke compiler flags to get the old behaviour (which may not even be possible, I don't know).

To emulate the old behaviour in your project you could use code like:

double my_scan(char const *s)
{
    double value = 0.0;

    if ( ! memcmp(s, "0X", 2) )
         sscanf(s, "%f", &value);

    return value;
}

or whatever would suit your use case. Perhaps you might end up writing your own tokenizer.



来源:https://stackoverflow.com/questions/47464440/sscanf0x80-9-f-value-sets-value-to-a-hex-float-not-a-float

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!