Float omzetten in meerdere bytes

De afronding van C laat vaak te wensen over.
In beide gevallen gaat het fout:

code:


#include <stdio.h>

int main(int argc, char* argv[]) {
    float f = 12345.0154;

    // 2 decimalen
    long Waarde = (long)(f * 100);
    long VoorDeKomma   = Waarde/100;
    long AchterDeKomma = Waarde%100;
    printf( "%ld.%02ld\n", VoorDeKomma, AchterDeKomma );
    // output: 12345.01

    // 3 decimalen
    Waarde = (long)(f * 1000);
    VoorDeKomma   = Waarde/1000;
    AchterDeKomma = Waarde%1000;
    printf( "%ld.%03ld\n", VoorDeKomma, AchterDeKomma );
    // output: 12345.016

}

compiler: gcc (Ubuntu 6.2.0-5ubuntu12)

Afronden kan per compiler verschillen. Meeste moderne gebruiken de IEC 60559. (dan zou deze uitslag inderdaad niet kloppen)

Arco - "Simplicity is a prerequisite for reliability" - www.arcovox.com

Ik gebruik eigenlijk zelden floats omdat ze mij te duur zijn.
Maar de TS wil dat blijkbaar wel, waarom dan niet ook printen als een float?

code:


    printf( "%.2f\n", 12345.0154 );
    // output  12345.02

of als je het in een buffer wil hebben:

code:


    char buf[20];
    sprintf( buf, "%.2f", 12345.0154 );

ik lees net:

Op 6 februari 2018 13:04:57 schreef Dennis_:
@mbbneon ik wil het op een display laten zien, alleen werkt mijn multiplexing code met een byte voor elke digit.

dus:

code:


    char buf[20];
    memset( buf, 20, 0 ); // als je iets in een loopje 
                          // doet dan eerst buffer legen
    sprintf( buf, "%.02f", 12345.0154 );
    char *p = buf;
    while( *p ) {
        printf( "%c", *p ); // doe iets met *p bijvoorbeeld multiplexen
        p++;
    }     

volatile en optimalisatie maken weinig verschil. De omvang van de code verandert wel iets maar in de snelheid is daar weinig van terug te vinden.

En als je de assembly bekijkt dan wordt er toch echt met floats gerekend:

code:


         DisplayValue = AdcValue * Gain + Offset;
 1e6:	69 a5       	ldd	r22, Y+41	; 0x29
 1e8:	7a a5       	ldd	r23, Y+42	; 0x2a
 1ea:	8b a5       	ldd	r24, Y+43	; 0x2b
 1ec:	9c a5       	ldd	r25, Y+44	; 0x2c
 1ee:	29 8d       	ldd	r18, Y+25	; 0x19
 1f0:	3a 8d       	ldd	r19, Y+26	; 0x1a
 1f2:	4b 8d       	ldd	r20, Y+27	; 0x1b
 1f4:	5c 8d       	ldd	r21, Y+28	; 0x1c
 1f6:	cd 88       	ldd	r12, Y+21	; 0x15
 1f8:	de 88       	ldd	r13, Y+22	; 0x16
 1fa:	ef 88       	ldd	r14, Y+23	; 0x17
 1fc:	f8 8c       	ldd	r15, Y+24	; 0x18
 1fe:	8b d1       	rcall	.+790    	; 0x516 <__mulsf3>
 200:	a7 01       	movw	r20, r14
 202:	96 01       	movw	r18, r12
 204:	43 d0       	rcall	.+134    	; 0x28c <__addsf3>
 206:	6d a3       	std	Y+37, r22	; 0x25
 208:	7e a3       	std	Y+38, r23	; 0x26
 20a:	8f a3       	std	Y+39, r24	; 0x27
 20c:	98 a7       	std	Y+40, r25	; 0x28

Alleen 'systeemeigen' variabelen? Dan kun je niet veel meer doen. Een long int of een uint16_t zijn net zo min systeemeigen als een float.

Volgens mij is er ook niks mis met het gebruik van library funkties. Voor de integer versie had ik ook een library funktie gebruikt voor de conversie (ltoa()).

Maar als je geen library wilt gebruiken, dan kun je ook voor floats een eigen funktie schrijven. Bijvoorbeeld zo:

code:


void ftoa(float x, char *pBuffer, int NrDigits)
{
   x = x / 10000.;          //< Max x = 9999.
   for(int i = 0;  ; i++)
   {
      if(i < NrDigits)
      {  uint8_t c;
         x *= 10; 
         c = x;
         x -= c;
         pBuffer[i] = '0' + c;      
      }
      else
      {  pBuffer[i] = 0;
         break;
      }
   }
}

Edit:
Die funktie heeft ook zijn beperkingen natuurlijk. Werkt alleen voor positieve getallen kleiner dan 10000.0 bijvoorbeeld. Maar is wel efficient in code. Als ik deze funktie gebruik ipv dtostrf(), dan is het hele programma niet groter dan 0x6F2 (1778) bytes.