Re: what is the best way of passing floats into a string
- From: Pascal Bourguignon <pjb@xxxxxxxxxxxxxxxxx>
- Date: Sat, 20 Jan 2007 17:05:51 +0100
atv <alef@xxxxxxxxx> writes:
I need to pass a float into a string (o_tail->y). I tried doing:
o_tail->y=malloc(sizeof(float)+1);
snprintf(o_tail->y,sizeof(float)+1,"%f",1.0);
I do not null-terminate as snprintf takes care of this (according to
my man page). That's why i also account for the extra +1. However,
this only copies 4 bytes.
Indeed, the size of the _decimal_ _representation_ of a floating point
number is totally unrelated to the number of bit used to represent
this number in floating point. When you use the type float, you use a
fixed number of bits (eg 32 bits) to represent a subset of the decimal
numbers. All the numbers that can be represented as a float are
represented with the same number of bits. As you can see if you read
it, sizeof(float) is not dependant on the number, only on the type
float. This is a property of the IEEE 754 representation of these
numbers.
On the other hand, when we represent numbers in the usual notation, in
base ten with a comma, and possibly an exponent factor, we have sizes
of the notations that depend on the values of these numbers. For
example, for one, we only need to write a one-character string: "1",
but for a thousand, we need to write a four-character string: "1000".
So, the question is how much characters you will need to write a given
floating point number? Actually, that depends, you can write it as
wide as you want:
...
0.0000000000000001
0.000000000000001e-1
0.00000000000001e-2
0.0000000000001e-3
...
0.1e-15
1.0e-16
10.0e-17
100.0e-18
1000.0e-19
10000.0e-20
...
1000000000000000.0e-31
...
The minimum size for a simple number like 1 or 2 is one character.
However we can easily know the maximum minimum size.
IEEE 754 numbers have 24 significant bits of binary mantissa. This
means that the mantissa of a floating point numbers can have at most
ceiling(log10(2^24)) = 8 decimal digits. Add one for the sign, one
for the decimal point, one for the E and three more for the exponent
value (which, being of only 7-bits for a base-two exponent (sign
excluded) has a maximum value of ±38 base-ten exponent
(floor(log10(2^127))=38), which gives a total of 8+1+1+3=13
characters to represent in decimal exponent notation a decimal number
stored as IEEE 754 floating point.
But the easiest way to determine the size needed to format a number,
is to ask snprintf itself, because your compiler/target host may have
a different sort of floating point numbers!
int length_of_representation(double n,const char* format){
char buffer[1];
return(snprintf(buffer,1,format,n));
/* Note if you want to be compatible with old glibc, you'll
have to test for -1 and loop, increasing the buffer size
until you get a positive number less than the buffer size.
See snprintf man page on GNU/linux. */
}
char* format_decimal_representation(double n){
int allocsize=1+length_of_representation(n,"%f");
char* buffer=malloc(allocsize);
snprintf(buffer,allocsize,"%f",n);
return(buffer);
}
Note that these C library functions (printf, ceil, log, etc) take
double arguments, not float, so it is better to use double arguments
in the above functions (and node that sizeof(double) is generally the
double of sizeof(float), but this doesn't change the size of the
decimal representation of a number that can be represented exactly in
both float and double).
If i have a float number of say -10.0 only -10. would get
copied. How do i account for the extra precision? I tried using the
format specifier to widen this, but that does not seem to work.
So i tried sprintf (which i'd rather not use because of security
implications) and when i do:
o_tail->x=malloc(sizeof(float)+1);
sprintf(o_tail->x,"%f",-10.0);
I get a nice result of -10.000000 in my char * string. Why does it do
this. Is it even correct as i do not quite understand this, because i
reserved memory for sizeof(float)+1 which should be 5 bytes on my
machine, yet it can write -10.000000 (8-9) chars into my char *
string.
It is not correct. You have overwritten memory that you haven't
allocated. You're program is about to crash, or just behave randomly.
Does sprintf terminate a supplied string "%f %d" as well by
the way?
sprintf doesn't terminate the supplied format string. If you pass a
string starting with "%f %d... and not terminated, you will get
whatever garbage there is after this format string up until the next
null byte or the next illegal memory access. Always terminate your
strings!
But sprintf will terminate any string it writes. It will also happy
overwrite any memory following the buffer if you don't give it a
buffer big enough.
That's why you should use snprintf.
You can also use the %g format specifier, which will choose the %f or
%e according to the value of the number.
And of course, you can specific the width of the number and the width
of the decimal parts, to get a fixed width representation:
#include <stdio.h>
int main(void){
double n[]={0.0,1.0,1.234567890123456e38,-1.234567890123456e-38};
int i;
printf("\n");
for(i=0;i<sizeof(n)/sizeof(n[0]);i++){
printf("%g\n",n[i]);
}
printf("\n");
for(i=0;i<sizeof(n)/sizeof(n[0]);i++){
printf("%15.8e\n",n[i]);
}
return(0);
}
0
1
1.23457e+38
-1.23457e-38
0.00000000e+00
1.00000000e+00
1.23456789e+38
-1.23456789e-38
--
__Pascal Bourguignon__ http://www.informatimago.com/
In a World without Walls and Fences,
Who needs Windows and Gates?
.
- Follow-Ups:
- References:
- Prev by Date: Re: what is the best way of passing floats into a string
- Next by Date: Re: what is the best way of passing floats into a string
- Previous by thread: Re: what is the best way of passing floats into a string
- Next by thread: Re: what is the best way of passing floats into a string
- Index(es):
Relevant Pages
|