Sprintf (buf, "..20g", x) // How big is buf?

I convert double values ​​to string as follows:

std::string conv(double x) { char buf[30]; sprintf(buf, "%.20g", x); return buf; } 

I hard coded the buffer size to 30, but I'm not sure if this is enough for all cases.

  • How do I know the maximum buffer size I need?
  • Does accuracy increase (and therefore, should the buffer increase) when switching from 32 bits to 64?

PS: I can't use ostringstream or boost::lexical_cast to improve performance (see this )

+3
source share
5 answers

I set the buffer size to 30, but I'm not sure if this is enough for all cases.

It. % .20g indicates 20 digits in the mantissa. add 1 for the decimal point. 1 for the (possible) sign, 5 for "e + 308" or "e-308", worst case indicator. and 1 to complete the null value.

20 + 1 + 1 + 5 + 1 = 28.

Does accuracy increase (and therefore, should the buffer increase) when switching from 32 bits to 64?

No.

Double the same size in both architectures. If you declare your variables long, then you may have another digit in the exponent "e + 4092", which is still placed in the buffer with 30 characters. But only on X86 and only on older processors.

The long double is an obsolete 80-bit form of floating-point value, which was the native 486 FPU format. This FPU architecture did not scale very well and has since been discarded in the direction of SSE style instructions, where the largest possible floating point value is 64-bit double.

This is a long way to say that a buffer of 30 characters will always be sufficient if you continue to limit the mantissa in your listing to 20 digits.

+3
source

printf("%.20g", 1.79769e+308); is 1.7976900000000000632e+308 , 27 bytes, including the final \ 0. I would choose 64 or 128 to be sure.

(Since it is on the stack and released immediately after you can also go with large buffers, even 2048 bytes, without problems in non-embedded applications)

Also, are you sure that your program lexical_cast is lexical_cast ..? Doing what you do seems very silly to me.

+3
source

I seem to remember that if you call sprintf with a NULL destination, it does nothing. However, it returns the number of characters that he "wrote." If I am right (and I cannot find a source for this), you can do:

 // find the length of the string int len = sprintf(NULL, fmt, var1, var2,...); // allocate the necessary memory. char *output = malloc(sizeof(char) * (len + 1)); // yes I know that sizeof(char) is defined as 1 but this seems nicer. // now sprintf it after checking for errors sprintf(output, fmt, var1, var2,...); 

Another option is to use snprintf , which allows you to limit the output length:

 #define MAX 20 /* or whatever length you want */ char output[MAX]; snprintf(output, MAX, fmt, var1, var2,...); 

snprintf takes the size of the buffer as an argument and does not allow the output string to exceed this size.

+3
source

Here, for any system, you may need a program to print the number of digits needed for the maximum and minimum double values:

 #include <float.h> #include <stdio.h> int main(void) { double m = DBL_MAX; double n = DBL_MIN; int i; i = printf("%.20g\n", m); printf("%d\n", i); i = printf("%.20g\n", n); printf("%d\n", i); return 0; } 

For me, he prints:

 1.7976931348623157081e+308 27 2.2250738585072013831e-308 27 

Since 27 includes a new line, but does not include a trailing 0 for lines, I would say that 27 should be enough on this system. For long double answer seems to be 27 and 28 for LDBL_MAX and LDBL_MIN respectively on my system.

The man page (in my sprintf says %g about this:

The double argument is converted to f or e style (or F or E for G conversion). Accuracy determines the number of significant figures. If there is no accuracy, 6 digits are issued; if the accuracy is zero, it is treated as 1. Style e is used if the conversion rate is less than -4 or higher than or equal to accuracy. Trailing zeros are removed from the fractional part of the result; a decimal point appears if it follows at least one digit.

A similar wording is found in standard C.

So, I think that you will be safe if you use the output from the above program as the size of your array.

0
source

If you are on a platform that supports POSIX or C99, you can use snprintf to calculate the size of the buffer you need. snprintf accepts a parameter indicating the size of the buffer that you are passing through; if the size of the string exceeds the size of this buffer, it truncates the output so that it fits into the buffer, and returns the amount of space that it should have placed for all the output. You can use the output of this to allocate a buffer that matches the exact correct size. If you just want to calculate the required buffer size, you can pass NULL as a buffer and a size of 0 to calculate how much space you need.

 int size = snprintf(NULL, 0, "%.20g", x); char *buf = malloc(size + 1); // Need the + 1 for a terminating null character snprintf(buf, size + 1, "%.20g", x); 

Remember free(buf) after you used it to avoid memory leaks.

The problem is that it will not work in Visual Studio, which still does not support C99. While they are something like snprintf , if the buffer went too small, it does not return the required size, but returns -1 , which is completely useless (and it does not accept NULL as a buffer, even with a length of 0 ).

If you don't mind truncating, you can simply use snprintf with a fixed-size buffer and make sure you don't overflow it:

 char buf[30]; snprintf(buf, sizeof(buf), "%.20g", x); 

Make sure you check your documents on the snprintf platform; in particular, some platforms cannot add trailing zero at the end of a line if the line is truncated, so you may need to do this yourself.

0
source

All Articles