How to use DBL_MANT_DIG to check strtod - c

Consider the following code:
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#include <errno.h>
#include <float.h>
int main (void) {
double val;
/* base b = 2; 2^DBL_MANT_DIG */
/* decimal digits log10(2^DBL_MANT_DIG) */
/*const char *str = "9007199254740992";*/
const char *str = "9007199254740993";
errno = 0;
val = strtod(str, NULL);
printf("%d\n", DBL_MANT_DIG );
if (errno == ERANGE) {
printf("error\n");
} else {
printf("%f\n", val);
}
return 0;
}
This returns:
53
9007199254740992.000000
Since str has a string number that has more significant digits than the my machine can handle, how does one use DBL_MANT_DIG or the log10(2^DBL_MANT_DIG) version of it to check that the result of val is correct?

You don't use those to check that the conversion is exact.
Here's one way of how to do it.
Another way is to find out how many decimal digits after the decimal point are there in the resultant double, do sprintf() using that as the precision and compare its output with the original string.

Related

How to convert IPv4 address to decimal

Is there a function to convert an IPv4 address in dotted decimal notation (e.g., "192.168.0.1") into a decimal value?
There are several questions similar to this, but all are looking for or have answers that involve writing a function to solve the answer. I'm looking for an existing function in a library.
The C standard library doesn't have such a function. It's simple enough that this shouldn't be a problem. Here's some example code using strtoul() for parsing:
int parseIpv4Address(uint32_t *addr, const char *addrStr)
{
const char *p = addrStr;
char *endp;
unsigned long a = strtoul(p, &endp, 10);
if (*endp != '.') return -1;
unsigned long b = strtoul(p = endp+1, &endp, 10);
if (*endp != '.') return -1;
unsigned long c = strtoul(p = endp+1, &endp, 10);
if (*endp != '.') return -1;
unsigned long d = strtoul(p = endp+1, &endp, 10);
if (*endp) return -1;
*addr = (uint32_t) ((a << 24) | (b << 16) | (c << 8) | d);
return 0;
}
as unsigned long must have at least 32 value bits, there's no special care (casting) needed for the shifts here.
It doesn't validate the range of the individual parts, if you need this, you should be able to add it without problems.
You could use something like inet_pton which is pretty straight forward to use as you can see from the example code below.
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(void)
{
struct in_addr result;
if(inet_pton(AF_INET,"127.0.0.1",&result))
{
printf("%08x\n",result.s_addr);
}
else
{
printf("Failed\n");
}
return(0);
}
The number is in network byte order, but if you want it in host byte order, you can use ntohl to convert it.
It turns out that Winsock 2 provides the inet_addr() function, which simply returns the equivalent decimal value for the address.
#include <stdio.h>
#include <winsock2.h>
int main(void)
{
char* pAddr = "192.168.0.1";
unsigned long value = inet_addr(pAddr);
if (value == INADDR_NONE)
printf("Invalid format of IP address");
else
printf("Decimal representation of %s is: %lu", pAddr, value);
return 0;
}

Strtol doesn't set errno on overflow conversion

My strtol function fails to set errno during overflown conversion.
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <getopt.h>
#include <errno.h>
#include <stdlib.h>
int main(int argc, char **argv) {
errno = 0;
int e = strtol("1000000000000000", NULL, 10);
printf("%d %d\n", errno, e);
return 0;
}
returns
0 -1530494976
What do I do wrong?
Compiler
gcc (Ubuntu 4.9.2-10ubuntu13) 4.9.2
Options
gcc -Wall -std=gnu99 -O2
There is nothing wrong with the implementation of strtol() but there is with your code.
The return type of this function is long (see the trailing l) and apparently the value 1000000000000000 can be represented by the long integer type. However the return value is assigned to e whose type is int which is unable to represent this value. What then happens is implementation-defined.
So change int e to long e and "%d %d\n" to "%d %ld\n". If you want to keep it as int, then you have to check if the value is outside of its range of representable values by yourself:
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <limits.h> // for INT_{MIN,MAX}
int
main(void)
{
errno = 0;
long f = strtol("1000000000000000", NULL, 10);
if (errno == ERANGE) {
puts("value not representable by long (or int)");
} else if (f < INT_MIN || f > INT_MAX) {
puts("value not representable by int");
} else {
int e = f;
printf("%d\n", e);
}
}
It seems like both Microsoft [1] and Apple [2] implementations have the setting of errno commented out.
[1] http://research.microsoft.com/en-us/um/redmond/projects/invisible/src/crt/strtol.c.htm
[2] http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/libkern/strtol.c

Convert ASCII string to int/float/long

I have this code to convert an ASCII string to and int, float, or double. However, it prints "42" for all of them. Where did I go wrong? The syntax looks correct, no warnings.
#include <stdlib.h>
int main(void)
{
char *buf1 = "42";
char buf2[] = "69.00";
int i;
double d;
long l;
i = atoi(buf1);
l = atol(buf1);
d = atof(buf2);
printf("%d\t%d\t%d\n", i, l, d);
return 0;
}
First, you should avoid use of the ato* functions (ie: atoi, atof, etc), because there are cases where the conversion failed, and it just returns zero, so you have no way to know if the input was really a string representing zero, or if it was due to a processing error. If you modify the example below, for example, and change buf2 to "z16", you will get a warning you can handle. atoi would not let you know about that error.
Second, your format specifiers are incorrect in your printf call. Your compiler should have generated warnings about this.
Please refer to the example below for a working version that includes conversion error handling. Not that the explicit casting of strtol to (int) in my example does allow for a potential integer overflow. Try making buf1 a large number, and see what happens.
Good luck!
Code Listing
#include <stdio.h> /* printf */
#include <stdlib.h> /* strtox */
#include <errno.h> /* error numbers */
#define BASE (10) /* use decimal */
int main(void) {
char* errCheck;
char *buf1 = "42";
char *buf2 = "16";
char buf3[] = "69.00";
int i;
double d;
long l;
/* Handle conversions and handle errors */
i = (int)strtol(buf1, &errCheck, BASE);
if(errCheck == buf1) {
printf("Conversion error:%s\n",buf1);
return EIO;
}
l = strtol(buf2, &errCheck, BASE);
if(errCheck == buf2) {
printf("Conversion error:%s\n",buf2);
return EIO;
}
d = strtod(buf3, &errCheck);
if(errCheck == buf3) {
printf("Conversion error:%s\n",buf3);
return EIO;
}
printf("%d\t%ld\t%lf\n", i, l, d);
return 0;
}
Change
printf("%d\t%d\t%d\n", i, l, d);
to
printf("%d\t%ld\t%f\n", i, l, d);
Please don't use ato* functions as ato* functions has been deprecated by strto* and should not be used in new code.
The problem with ato* is, if the converted value is out of range it causes undefined behavior.
For more information check here.

How to convert string to int64_t?

How to convert program parameter from argv to int64_t? atoi() is suitable only for 32 bit integers.
There are a few ways to do it:
strtoll(str, NULL, 10);
This is POSIX C99 compliant.
you can also use strtoimax; which has the following prototype:
strtoimax(const char *str, char **endptr, int base);
This is nice because it will always work with the local intmax_t ... This is C99 and you need to include <inttypes.h>
A C99 conforming attempt.
[edit] employed #R. correction
// Note: Typical values of SCNd64 include "lld" and "ld".
#include <inttypes.h>
#include <stdio.h>
int64_t S64(const char *s) {
int64_t i;
char c ;
int scanned = sscanf(s, "%" SCNd64 "%c", &i, &c);
if (scanned == 1) return i;
if (scanned > 1) {
// TBD about extra data found
return i;
}
// TBD failed to scan;
return 0;
}
int main(int argc, char *argv[]) {
if (argc > 1) {
int64_t i = S64(argv[1]);
printf("%" SCNd64 "\n", i);
}
return 0;
}
Users coming from a web search should also consider std::stoll.
It doesn't strictly answer this original question efficiently for a const char* but many users will have a std::string anyways. If you don't care about efficiency you should get an implicit conversion (based on the user-defined conversion using the single-argument std::string constructor) to std::string even if you have a const char*.
It's simpler than std::strtoll which will always require 3 arguments.
It should throw if the input is not a number, but see these comments.
strtoll converts it to a long long which is usually a 64-bit int.
Doing this 100% portably is a little bit tricky. long long is required to be at least 64 bits, but need not necessarily be twos complement, so it might not be able to represent -0x7fffffffffffffff-1, and thus using strtoll could have a broken corner case. The same issue applies to strtoimax. What you could do instead is consume leading space (if you want to allow leading space) and check for the sign first, then use strtoull or strtoumax, either of which is required to support values up to the full positive range of int64_t. You can then apply the sign:
unsigned long long x = strtoull(s, 0, 0);
if (x > INT64_MAX || ...) goto error;
int64_t y = negative ? -(x-1)-1 : x;
This logic is written to avoid all overflow cases.
How to convert string to int64_t?
The simplest
#include <stdlib.h>
int64_t value = atoll(some_string); // Lacks error checking. UB on overflow.
Better
long long v = strtoll(s, NULL, 0); // No reported errors, well defined on overflow.
Robust: Create a helper function to detect all problems.
#include <stdbool.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <stdint.h>
// Return error flag
bool my_strtoi64(int64_t *value, const char *s) {
// Maybe add a s==NULL, value==NULL checks.
char *endptr;
errno = 0;
long long v = strtoll(s, &endptr, 0);
// Optional code for future growth of `long long`
#if LLONG_MIN < INT64_MIN || LLONG_MAX > INT64_MAX
if (v < INT64_MIN) {
v = INT64_MIN;
errno = ERANGE;
} else if (v > INT64_MAX) {
v = INT64_MAX;
errno = ERANGE;
#endif
*value = (int64_t) v;
if (s == endptr) { // No conversion, v is 0
return true;
}
if (errno == ERANGE) { // Out of range
return true;
}
if (errno) { // Additional implementations specific errors
return true;
}
while (isspace(*(unsigned char* )endptr)) { // skip trailing white-space
endptr++;
}
if (*endptr) { // Non-numeric trailing text
return true;
}
return false; // no error
}
This worked for me with a different int64 type, and I like the clean C++ style:
std::istringstream iss(argv[i]);
int64_t i64;
iss >> i64;
You may get an compile error: operartor<<... is not defined.
And I don't know what happens, if argv[i] contains "HALLO".

GCC division truncates (rounding problem)

Using GCC on the Ubuntu Linux 10.04, I have unwanted rounding after a division.
I tried:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void FormatReading(int temp)
{
double reading = temp / 100;
printf("%f\n",reading); /* displays 226.000000, was expecting 226.60 */
}
int main(void)
{
FormatReading(22660);
return 0;
}
It was suggested to me to try:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void FormatReading(int temp)
{
long reading = temp ;
reading = reading / 100;
printf("%3.2ld\n",reading); /* displays 226 */
}
int main(void)
{
FormatReading(22660);
return 0;
}
I also tried:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void FormatReading(int temp)
{
long reading = temp ;
double reading2 = reading / 100;
printf("%3.2f\n",reading2); /* displays 226.00 */
}
int main(void)
{
FormatReading(22660);
return 0;
}
I also tried the round function using include math.h with compiler tag -lm in various ways, but did not find what I was looking for.
Any help greatly appreciated.
Best regards,
Bert
double reading = temp / 100.0;
^^
temp / 100 is an integer division - that you assign the result to a double doesn't change this.
You are using integer division which always gives integral results rather than fractions, and then the result is being assigned to a double. Divide by 100.0 instead of 100 to get the behavior you want.

Resources