LynxOS strtod not the same as Linux - c

It seems that LynxOS's implementation of strtod doesn't handle all the same cases as that of Linux, or for that matter Solaris. The problem I have is that I am trying to parse some text that can have decimal or hexadecimal numbers in it.
On Linux I call
a = strtod(pStr, (char **)NULL);
and I get the expected values in a for input strings such as 1.234567 and 0x40.
On LynxOS, the decimal numbers parse correctly, but the hex parses simply as 0 due to stopping when it hits the 'x'. Looking at the man pages, it seems that LynxOS's strtod only supports decimal strings in the input.
Does anyone here know of an alternative that will work on both Lynx and Linux?

Quote from the Standard (7.20.1.3) ( http://www.open-std.org/JTC1/sc22/wg14/www/docs/n1256.pdf )
The expected form of the subject sequence is an optional plus or minus sign, then one of
the following:
— a nonempty sequence of decimal digits optionally containing a decimal-point
character, then an optional exponent part as defined in 6.4.4.2;
— 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;
— [...]
So, the compiler you're using on LynxOS is not a C99 compiler.
My copy of the C89 Standard has no reference to the 0x prefix:
4.10.1.4 The strtod function
[...]
The expected form of the subject sequence is an optional plus or
minus sign, then a nonempty sequence of digits optionally containing a
decimal-point character, then an optional exponent part [...]

strtod takes 3 arguments, not two. If you had prototyped it by including the correct header (stdlib.h), your compiler would have issued an error. Since you're calling a function with the wrong signature, your program has undefined behavior. Fix this and everything will be fine.

Related

difference between isgraph() and iscntrl() functions:

Regarding the functions isgraph() and iscntrl():
What is the difference between these functions in C?
Can anybody explain what is the difference between them and in what situation they are used?
c 2018 7.4.1.6 2 says:
The isgraph function tests for any printing character except space (’ ’).
C 2018 7.4.1.4 2 says:
The iscntrl function tests for any control character.
C 2018 7.4 3 says:
The term printing character refers to a member of a locale-specific set of characters, each of which occupies one printing position on a display device; the term control character refers to a member of a locale-specific set of characters that are not printing characters.
Therefore, the isgraph characters, space, and the iscntrl characters form a partition of the set of characters: Each character in the set is in exactly one of those three subsets. So isgraph and iscntrl are complements except for the space character, which is neither of them.
There may be unsigned char codes that do not correspond to any characters in the locale-specific set of characters. Since such codes are not in the set of characters, neither isprint nor iscntrl returns true (non-zero) for them, nor do they represent a space.

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

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?
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.
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 …
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.
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;
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.
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.

Standard form in C

After a few hours of searching I have not been able to find an answer, but if this is a duplicate please point me in the correct direction.
Will a C program accept a standard form input into something like scanf("%f",&float); from the keyboard. Standard form is writing a number like 2400 as 2.4E3 if this helps you understand what I am asking.
I must stress this MUST be from the keyboard.
Yes.
scanf works identically, regardless of whether stdin is connected to a terminal, a file, or some other input stream.
The %a, %e, %f, %g scanf format codes all do the same thing: interpret the longest string which would be acceptable to strtod (§7.21.6.2/12). (And so do %A, %E, %F and %G -- paragraph 14 of the same clause.) The redundancy is because scanf formats accepts the same format codes as printf.
strtod accepts any of the following (§7.22.1.3/3):
a nonempty sequence of decimal digits optionally containing a decimal-point character, then an optional exponent part as defined in 6.4.4.2;
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;
INF or INFINITY, ignoring case
NAN or NAN(n-char-sequenceopt), ignoring case in the NAN part,…
Yes, it is being accepted from my terminal and also the IDEone interpreter (added it in stdin which is the same as "from the keyboard").
And probably a typo, but you have missed the "" in
scanf("%f", &float)

Understanding output of printf containing backslash (\012)

Can you please help me to understand the output of this simple code:
const char str[10] = "55\01234";
printf("%s", str);
The output is:
55
34
The character sequence \012 inside the string is interpreted as an octal escape sequence. The value 012 interpreted as octal is 10 in decimal, which is the line feed (\n) character on most terminals.
From the Wikipedia page:
An octal escape sequence consists of \ followed by one, two, or three octal digits. The octal escape sequence ends when it either contains three octal digits already, or the next character is not an octal digit.
Since your sequence contains three valid octal digits, that's how it's going to be parsed. It doesn't continue with the 3 from 34, since that would be a fourth digit and only three digits are supported.
So you could write your string as "55\n34", which is more clearly what you're seeing and which would be more portable since it's no longer hard-coding the newline but instead letting the compiler generate something suitable.
\012 is an escape sequence which represents octal code of symbol:
012 = 10 = 0xa = LINE FEED (in ASCII)
So your string looks like 55[LINE FEED]34.
LINE FEED character is interpreted as newline sequence on many platforms. That is why you see two strings on a terminal.
\012 is a new line escape sequence as others stated already.
(What might be, as chux absolute correct commented, different if ASCII isn't the used charset. But anyway it is in this notation an octal digit.)
this is meant by standard as it says for c99 in ISO/IEC 9899
for:
6.4.4.4 Character constants
[...]
3 The single-quote ', the double-quote ", the question-mark ?, the backslash \, and
arbitrary integer values are representable according to the following table of escape
sequences:
single quote' \'
double quote" \"
question mark? \?
backslash\ \
octal character \octal digits
hexadecimal character \x hexadecimal digits
And the range it gets bound to:
Constraints
9 The value of an octal or hexadecimal escape sequence shall be in the range of
representable values for the type unsigned char for an integer character constant, or
the unsigned type corresponding to wchar_t for a wide character constant.

How to escape from hex to decimal

I apologise if this is an obvious question. I've been searching online for an answer to this and cannot find one. This isn't relevant to my code per se, it's a curiosity on my part.
I am looking at testing my function to read start and end bytes of a buffer.
If I declare a char array as:
char *buffer;
buffer = "\x0212\x03";
meaning STX12ETX - switching between hex and decimal.
I get the expected error:
warning: hex escape sequence out of range [enabled by default]
I can test the code using all hex values:
"\x02\x31\x32\x03"
I am wanting to know, is there a way to escape the hex value to indicate that the following is a decimal value?
will something like this work for you ?
char *buffer;
buffer = "\x02" "12" "\x03";
according to standard:
§ 5.1.1.2 6. Adjacent string literal tokens are concatenated.
§ 6.4.4.4 3. and 7. Each octal or hexadecimal escape sequence is the longest sequence of characters that can constitute the escape sequence.
the escape characters:
\' - single quote '
\" - double quote "
\? - question mark ?
\ - backslash \
\octal digits
\xhexadecimal digits
So the only way to do it is concatenation of strings with the precompiler concatenation ( listing them one after another).
if you want to know more how the literals are constructed by compiler look at §6.4.4.4 and §6.4.5 they describe how to construct the character literals and string literals respectively.
You can write
"\b12"
to represent a decimal value. Altough you need to use space after hex values for it to work.
buffer = "\x02 \b12\x03";
Or just 12
buffer = "\x02 12\x03";
Basically you need to add a blank character after your hex values to indicate that it's a new value and not the same one
No, there's no way to end a hexadecimal escape except by having an invalid (for the hex value) character, but then that character is of course interpreted in its own right.
The C11 draft says (in 6.4.4.4 14):
[...] a hexadecimal escape sequence is terminated only by a non-hexadecimal character.
Octal escapes don't have this problem, they are limited to three octal digits.
You can always use the octal format. Octal code is always 3 digits.
So to get the character '<-' you simple type \215

Resources