Forums

(arm-elf-gcc and friends) Bug in floating point conversion/formatting ... ?

Started by Unknown February 2, 2004
Lately I have had a lot of trouble with the behaviour in a program I'm 
working on. Finally now I'm able to reproduce some weird behaviour wrt. 
floating point conversion. See the code snippet below which is the true 
code. The "0" prefix comes from the send_n function.

I am running the arm-elf-gcc toolchain, uC is an Atmel AT91R40807:

arm-elf-gcc
	ver 2.96
arm-elf-as
arm-elf-ld
arm-elf-objcopy
	ver 2.10


(Sorry, I don't know which library versions I use, and also don't know 
how to find it out. Please advise.)

When I run the code below the following gets printed

0   Check :   39.37
0   Check : 39.374996
0   Check :   30.00
0   Check : 39.375000
0   Check :   39.38
0   Check : 39.375004

If I change the formatting from %7.2f to %7.3f everything works like a 
charm, and the following is printed on the serial terminal

0   Check :  39.375
0   Check : 39.374996
0   Check :  39.375
0   Check : 39.375000
0   Check :  39.375
0   Check : 39.375004

I have had no luck googling for a solution/explanation, but of course, I 
might have been looking in the wrong direction.

All and any suggestions are welcome. Also, please don't hesitate to ask 
for further information.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void showfloatbug(int n)
{
   union longfloat {
     float flow;
     unsigned long l;
   } u;

   u.l = 0x421d7fff;
   snprintf(dbuf,sizeof(dbuf),"   Check : %7.2f",u.flow);
   send_n(n,dbuf);
   snprintf(dbuf,sizeof(dbuf),"   Check : %f",u.flow);
   send_n(n,dbuf);

   u.l = 0x421d8000;
   snprintf(dbuf,sizeof(dbuf),"   Check : %7.2f",u.flow);
   send_n(n,dbuf);
   snprintf(dbuf,sizeof(dbuf),"   Check : %f",u.flow);
   send_n(n,dbuf);

   u.l = 0x421d8001;
   snprintf(dbuf,sizeof(dbuf),"   Check : %7.2f",u.flow);
   send_n(n,dbuf);
   snprintf(dbuf,sizeof(dbuf),"   Check : %f",u.flow);
   send_n(n,dbuf);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

BR,
-- 
Torbj�rn Heltne
AM Elektronikk AS

Torbj�rn Heltne wrote:

> When I run the code below the following gets printed > > 0 Check : 39.37 > 0 Check : 39.374996 > 0 Check : 30.00 > 0 Check : 39.375000 > 0 Check : 39.38 > 0 Check : 39.375004
I just want to point out where the problem is visible, in the 3rd "Check" line here, where 30.00 is printed instead of 39.38. TIA, -- Torbj�rn Heltne AM Elektronikk AS
Torbj�rn Heltne wrote:
> Lately I have had a lot of trouble with the behaviour in a program I'm > working on. Finally now I'm able to reproduce some weird behaviour wrt. > floating point conversion. See the code snippet below which is the true > code. The "0" prefix comes from the send_n function. > > I am running the arm-elf-gcc toolchain, uC is an Atmel AT91R40807: > > arm-elf-gcc > ver 2.96 > arm-elf-as > arm-elf-ld > arm-elf-objcopy > ver 2.10 > > > (Sorry, I don't know which library versions I use, and also don't know > how to find it out. Please advise.) >
This is a library problem - the print formatter conversion is there. As you're feeding in the float patterns in hex, the compiler has little to do with them. To be sure, check the generated assembly code with the -S switch to the GCC. Your compiler is pretty old - the ARM code generation was completely rebuilt for GCC 3.x, please update if possible. The ELF support on 2.96 was kludged on to provide a minimal support for ARM-based Linux versions. Also, version 2.96 is *not* a genuine GCC version at all, it is a Red Hat special known to have problems. For details, have a look at the GNU pages <http://www.gnu.org/>. HTH Tauno Voipio tauno voipio @ iki fi
Torbj&#2013266168;rn Heltne wrote:
>
... snip ...
> > When I run the code below the following gets printed > > 0 Check : 39.37 > 0 Check : 39.374996 > 0 Check : 30.00 > 0 Check : 39.375000 > 0 Check : 39.38 > 0 Check : 39.375004 > > If I change the formatting from %7.2f to %7.3f everything works > like a charm, and the following is printed on the serial terminal > > 0 Check : 39.375 > 0 Check : 39.374996 > 0 Check : 39.375 > 0 Check : 39.375000 > 0 Check : 39.375 > 0 Check : 39.375004 > > I have had no luck googling for a solution/explanation, but o >f course, I might have been looking in the wrong direction. > > All and any suggestions are welcome. Also, please don't hesitate > to ask for further information. > > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > void showfloatbug(int n) > { > union longfloat { > float flow; > unsigned long l; > } u; > > u.l = 0x421d7fff; > snprintf(dbuf,sizeof(dbuf)," Check : %7.2f",u.flow); > send_n(n,dbuf); > snprintf(dbuf,sizeof(dbuf)," Check : %f",u.flow); > send_n(n,dbuf); > > u.l = 0x421d8000; > snprintf(dbuf,sizeof(dbuf)," Check : %7.2f",u.flow); > send_n(n,dbuf); > snprintf(dbuf,sizeof(dbuf)," Check : %f",u.flow); > send_n(n,dbuf); > > u.l = 0x421d8001; > snprintf(dbuf,sizeof(dbuf)," Check : %7.2f",u.flow); > send_n(n,dbuf); > snprintf(dbuf,sizeof(dbuf)," Check : %f",u.flow); > send_n(n,dbuf); > }
While it is not impossible that a system bug exists, your code is doubly flawed. First, you cannot legally extract a value from a union in a different form from that you put in. I am ignoring the undefined send() and dbuf above, I assume they have some meaning to you. I am also ignoring the lack of suitable #includes to define snprintf, etc. The legitimate way to access the bytes of something is via an unsigned char * pointer. The reverse mechanism MAY work, in fact usually will, but is not guaranteed. char getbyte(void *thingptr, int which) { unsigned char *p = thingptr; return p[which]; } Something that may well be happening to you is that floats and integers are stored in different registers, have different internal lengths, etc. You are not accessing what you think you are. You can also create: void putbyte(void *thingptr, int which, const unsigned char ch) { unsigned char *p = thingptr; p[which] = ch; } Now you could have your system above: int i, lgh; unsigned long l; float f; l = 0L; f = 39.37; for (i = 0; i < sizeof f; i++) putbyte(&f, i, (l >> (i * 8)) & 0xff); where the 8 and 0xff are only valid if CHAR_BIT is 8. This also has the (not guaranteed) assumption that sizeof(long) == sizeof(float). It should avoid peculiarities to do with actual implementation details. Note the &f to start with a pointer. The use of void* types will assure the correct transformations. The main point is: Don't take shortcuts around the standard. If fully standards compliant code fails, you have a right to complain, but not before. -- Chuck F (cbfalconer@yahoo.com) (cbfalconer@worldnet.att.net) Available for consulting/temporary embedded and systems. <http://cbfalconer.home.att.net> USE worldnet address!
Torbj&#2013266168;rn Heltne wrote:

(Something that I realize is pretty easy to misunderstand)
:-)

Let me re-visualize the actual problem this way:

~~~~~~
char dbuf[81];

void showfloatbug(int n)
{
   float f = 39.375;
   sprintf(dbuf,"   Check : %7.2f",f);
   /*Some code here to send dbuf on the serial port*/
   sprintf(dbuf,"   Check : %7.3f",f);
   /*Some code here to send dbuf on the serial port*/
}
~~~~~~

This is printed on my terminal:

    Check :   30.00
    Check :  39.375

-- 
Torbj&#2013266168;rn Heltne
AM Elektronikk AS

CBFalconer wrote:


> While it is not impossible that a system bug exists, your code is > doubly flawed.
I realize that my original posting was not to the point(!) describing the problem I'm facing. Sorry about that. I just posted an updated description which I hope is a little bit better. -- Torbj&#2013266168;rn Heltne AM Elektronikk AS
Tauno Voipio wrote:


> This is a library problem - the print formatter conversion is there. As > you're feeding in the float patterns in hex, the compiler has little to > do with them. To be sure, check the generated assembly code with the -S > switch to the GCC.
I just wrote in my reply to CBFalconer that my original posting was somewhat ...unclear. Please take a look at my updated posting.
> Your compiler is pretty old - the ARM code generation was completely > rebuilt for GCC 3.x, please update if possible. The ELF support on > 2.96 was kludged on to provide a minimal support for ARM-based > Linux versions. > > Also, version 2.96 is *not* a genuine GCC version at all, it is a > Red Hat special known to have problems. For details, have a look > at the GNU pages <http://www.gnu.org/>.
True. The toolchain I use is a bit old. -- Torbj&#2013266168;rn Heltne AM Elektronikk AS

Torbj=F8rn Heltne schrieb:
> ~~~~~~ > char dbuf[81]; >=20 > void showfloatbug(int n) > { > float f =3D 39.375; > sprintf(dbuf," Check : %7.2f",f); > /*Some code here to send dbuf on the serial port*/ > sprintf(dbuf," Check : %7.3f",f); > /*Some code here to send dbuf on the serial port*/ > } > ~~~~~~ >=20 > This is printed on my terminal: >=20 > Check : 30.00 > Check : 39.375 >=20
Hello, it looks like a bug in the used <stdio.h> library. You could try the types e or g instead of f, but the same bug might be=20 involved there too. sprintf(dbuf," Check : %7.2e",f); sprintf(dbuf," Check : %7.2g",f); Bye
Torbj&#2013266168;rn Heltne wrote:
> Torbj&#2013266168;rn Heltne wrote: > > (Something that I realize is pretty easy to misunderstand) > :-) > > Let me re-visualize the actual problem this way: > > ~~~~~~ > char dbuf[81]; > > void showfloatbug(int n) > { > float f = 39.375; > sprintf(dbuf," Check : %7.2f",f); > /*Some code here to send dbuf on the serial port*/ > sprintf(dbuf," Check : %7.3f",f); > /*Some code here to send dbuf on the serial port*/ > } > ~~~~~~ > > This is printed on my terminal: > > Check : 30.00 > Check : 39.375
We are still pointing to an obvious bug in the run-time print formatter in the library. Tauno Voipio tauno voipio @ iki fi PS. If you ever can, get rid of the GCC version 2.96. TV
Hello,

i just tried it out on LPC2106 with arm-elf-gcc version 3.3.2 and it gives
me the following result:
(slighty modified your code, haven't used it in a subroutine, but shouldn't
matter)

   Check :   39.38   Check :  39.375

So it look like to work !

Greetings,

         Martin


"Torbj&#2013266168;rn Heltne" <torbjorn.heltne@amelektronikk.no> schrieb im Newsbeitrag
news:401f53ef@news.broadpark.no...
> Torbj&#2013266168;rn Heltne wrote: > > (Something that I realize is pretty easy to misunderstand) > :-) > > Let me re-visualize the actual problem this way: > > ~~~~~~ > char dbuf[81]; > > void showfloatbug(int n) > { > float f = 39.375; > sprintf(dbuf," Check : %7.2f",f); > /*Some code here to send dbuf on the serial port*/ > sprintf(dbuf," Check : %7.3f",f); > /*Some code here to send dbuf on the serial port*/ > } > ~~~~~~ > > This is printed on my terminal: > > Check : 30.00 > Check : 39.375 > > -- > Torbj&#2013266168;rn Heltne > AM Elektronikk AS >