|
Home > Archive > Unix Programming > January 2007 > what is the best way of passing floats into a string
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
what is the best way of passing floats into a string
|
|
|
| Hello,
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. 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.
Does sprintf terminate a supplied string "%f %d" as well by the way?
In any case, any insight on this phenomena would be much appreciated.
Kind regards,
-alef
| |
| matevzb 2007-01-20, 1:17 pm |
| On Jan 20, 1:54 pm, atv <a...@xs4all.nl> wrote:
> Hello,
> 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. 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.
Firstly, the text representation of a float is usually wider than
sizeof(float), so _you_ will have to know how much you want to output.
The sizeof(float)+1 you passed to snprintf() includes the null
character, so it's able to output only 4 characters from the float.
Characters in the buffer should be:
'-', '1', '0', '.', '\0'
I tested this on a Mac OS X + gcc 3.3 and on a Windows + mingw gcc
3.4.5 and - strangely enough - the results are different:
Windows: str="-10.0"
Mac OS: str="-10."
Thus, the mingw gcc seems to be buggy and proves you shouldn't always
rely on your compiler output.
(when run in a debugger, the mingw version shows trash in the str
buffer)
> 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.
> Does sprintf terminate a supplied string "%f %d" as well by the way?
> In any case, any insight on this phenomena would be much appreciated.
Yes, sprintf() will always terminate the supplied buffer and you have
to ensure that enough space is available in the buffer. Since you
didn't, sprintf() happily wrote past the buffer boundry - this is
called buffer overflow. The bug is there, lurking, it just didn't
manifest itself yet.
--
WYCIWYG - what you C is what you get
| |
| matevzb 2007-01-20, 1:17 pm |
| On Jan 20, 2:54 pm, "matevzb" <mate...@gmail.com> wrote:
> The sizeof(float)+1 you passed to snprintf() includes the null
> character, so it's able to output only 4 characters from the float.
> Characters in the buffer should be:
> '-', '1', '0', '.', '\0'
> I tested this on a Mac OS X + gcc 3.3 and on a Windows + mingw gcc
> 3.4.5 and - strangely enough - the results are different:
> Windows: str="-10.0"
> Mac OS: str="-10."
> Thus, the mingw gcc seems to be buggy and proves you shouldn't always
> rely on your compiler output.
Actually, I made a mistake by not checking snprintf() return value.
mingw gcc returns -1, so the buffer contents aren't really a valid
string and shouldn't be used. So mingw gcc isn't broken and you
shouldn't rely on Usenet postings either =/
--
WYCIWYG - what you C is what you get
| |
| Jens Thoms Toerring 2007-01-20, 1:17 pm |
| atv <alef@xs4all.nl> wrote:
> I need to pass a float into a string (o_tail->y). I tried doing:
> o_tail->y=malloc(sizeof(float)+1);
I hope that you test the result of malloc() in your real code;-)
> 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. 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.
Simply don't use sizeof(float) - it only will give you as many
bytes as a floating point number needs for storing in _binary_
form on your machine, not as many bytes as you would need to
represent it as a string. Luckily, your use of snprintf() kept
you from writing past the end of the array you had malloc()ed.
If you insist on the %f' it's difficult to know how many
characters there are going to be - if the float value
is e.g. 1.0e230, you would need 237 characters to print
it out (with the default of 6 digits after the dot)! So
you basically have to take ceil() of log10() of the
(absolute value) of the number and, if this is non-negative,
add 9 to the result for a guess of how many characters you
may need as an upper limit (including a sign and a dot). If
the number is between +1 and -1 only 9 characters will do.
On the other hand, if you use '%g' (or '%e') instead you
won't print more than 12 characters (unless you raise the
precision manually to more than the default of 6 digits,
you have to calculate how many more you will need in that
case). Allocate that much memory (plus one extra for the
trailing '\0'), snprint() to it and, if you want to be
extremely tidy, realloc() afterwards if you should find
that not all the memory was needed as strlen(o_tail->y)
will tell you.
> 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.
And you have written past the end of the memory you allocated
(obviously, there's more than you allocated, but you don't own
it). Now all bets are of, expect mysterious crashes or strange
bugs some time later...
Regards, Jens
--
\ Jens Thoms Toerring ___ jt@toerring.de
\__________________________ http://toerring.de
| |
|
| >
> Simply don't use sizeof(float) - it only will give you as many
> bytes as a floating point number needs for storing in _binary_
> form on your machine, not as many bytes as you would need to
> represent it as a string. Luckily, your use of snprintf() kept
> you from writing past the end of the array you had malloc()ed.
Ok thanks everyone. But i do have 2 remaining questions:
if i do :
snprintf(o_tail->x,sizeof(float),"%f",0.10000);
my question is for argument 2 and 4. sizeof(float) is here just an
example, not nessarily the best method. what i wonder is, if you
specify the number of bytes to copy, say 6 am i not reaching beyond
what a float it's size is? normally a float is 4 bytes. if i tell
snprintf to copy 6 bytes, do i not get a access violation ?
The other question is:
o_tail->x=malloc(sizeof(float)+7);
snprintf(o_tail->x,sizeof(float)+7,"%f",(atof(o_tail->prev->x))+SECTOR_SIZE);
This line of code crashes. If i remove the atof function call, it is
ok. o_tail->prev->x is a char * ptr, and i know it is allocated. Why
does it crash?
| |
|
| pleas cancel the previous post. prev is null :-).
for whatever reason i now intend to find out.
| |
| Pascal Bourguignon 2007-01-20, 1:17 pm |
| atv <alef@xs4all.nl> 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?
| |
|
| On 2007-01-20 17:05:51 +0100, Pascal Bourguignon <pjb@informatimago.com> said:
Thank you for your detailed explanation Pascal.
Rgds,
alef
| |
| Logan Shaw 2007-01-20, 7:25 pm |
| atv wrote:
>
>
> Ok thanks everyone. But i do have 2 remaining questions:
>
> if i do :
> snprintf(o_tail->x,sizeof(float),"%f",0.10000);
>
> my question is for argument 2 and 4. sizeof(float) is here just an
> example, not nessarily the best method. what i wonder is, if you specify
> the number of bytes to copy, say 6 am i not reaching beyond
> what a float it's size is? normally a float is 4 bytes. if i tell
> snprintf to copy 6 bytes, do i not get a access violation ?
No, because you cannot tell snprintf() to copy bytes from a float, because
copying bytes is not what snprintf() *does*. snprintf() takes the float as
a parameter and produces a string that a human can understand. Like many
programs and routines, snprintf() produces an output that is larger than
its input.
Another way to think of it is in terms of information density. The string
value and the float represent the same thing[1], but one uses more space
to do it than the other. So, the string has a lower information density.
Consider the following two strings, which both represent the same value:
(1) "1982"
(2) "One thousand nine hundred and eighty two"
If I make a function to convert the first string into the second string,
the output is going to use more space than the input, even though
mathematically they are equivalent and contain the same information.
> The other question is:
> o_tail->x=malloc(sizeof(float)+7);
> snprintf(o_tail->x,sizeof(float)+7,"%f",(atof(o_tail->prev->x))+SECTOR_SIZE);
>
>
> This line of code crashes. If i remove the atof function call, it is ok.
> o_tail->prev->x is a char * ptr, and i know it is allocated. Why does it
> crash?
Maybe you haven't written anything into o_tail->prev->x yet. The atof()
function reads a string, and it will only stop when it sees a 0 byte
to terminate the string. If you haven't put a 0 byte within the space
you have allocated for o_tail->prev->x, the atof() has no way to know
to stop reading before it passes up the end of the space that was
legitimately allocated, and it will just start reading whatever garbage
it finds in the locations immediately in memory after the allocated space.
If it doesn't find a 0 byte for a long time, it might read some address
which doesn't even exist or which your process doesn't have read access to.
Then you'll get a crash.
- Logan
[1] Unless snprintf() has to truncate the value and lose a little
precision that way, but still, almost the same thing...
--
qoopqoopqoopqoopqoopqoopqoopqoopqoopqoop
qoopqpqoopqoopqoopqoopqoopqoop
dpqbdpqbdpqbdpqbdpqbdpqbdpqbdpqbdpqbdpqb
dpqbdbdpqbdpqbdpqbdpqbdpqbdpqb
qbdpqbdpqbdpqbdpqbdpqbdpqbdpqbdpqbdpqbdp
qbdpqpqbdpqbdpqbdpqbdpqbdpqbdp
doobdoobdoobdoobdoobdoobdoobdoobdoobdoob
doobdbdoobdoobdoobdoobdoobdoob
|
|
|
|
|