Re: what is the best way of passing floats into a string



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



Relevant Pages

  • Re: Gtk-2 File Chooser Modification Time Display
    ... There is a string default ... Well I experimented with setting the format string from %x to %c, ... GtkFileChooserDefault *impl; ... char buf; ...
    (comp.os.linux.misc)
  • String interpolation
    ... I'm trying to do some string interpolation -- that is, ... static char* expand(const char *format) ... strncat(string, format, pos); ...
    (comp.lang.c)
  • Re: String interpolation
    ... cigar" type questions -- as with all my others I have learnt tons, ... I'm trying to do some string interpolation -- that is, ... static char* expand(const char *format) ...
    (comp.lang.c)
  • Re: no clue
    ... > j is just the key in the outer hash. ... > 58 is the ascii number of the char ':' ... > while there is another ':' in the string ... and so could modify their format to make ...
    (comp.lang.ruby)
  • Re: sprintf counterparts?
    ... You're defeating the purpose of _snprintf by hard coding the size of the string in places where it is not necessary. ... What if someone comes along later and decides that allocating 1024 bytes is wasteful and changes only the "char" statement to, say, 256? ... size of a buffer really do use the compile-time size of the buffer. ...
    (microsoft.public.vc.language)