Festkommazahlen ausgeben

Aus der Mikrocontroller.net Artikelsammlung, mit Beiträgen verschiedener Autoren (siehe Versionsgeschichte)
Wechseln zu: Navigation, Suche

otuwfxdp.cpp - Ausgabe von vorzeichenlosen Festpunktzahlen 0...max.xx

Aufruf

FUNCT void out_uwfxdp(
         u16 w,            // Die Festpunktzahl, die ausgegeben werden soll
         u8 prec,        // Anzahl der auszugebenden Nachkommastellen
         u16 max_w,        // Der Wert, dem w=0xffff + 1 entspricht
         u8 dp)            // Kommastellen in max_w

Beispiel 1

Es sei eine Zahl w in 16 Bit dargestellt. Diese Zahl reiche 0 bis 65535. Dies seien z.B: 16 Bit von einem 16-Bit-ADC (ohne Vorzeichen) oder von einem 10-Bit ADC, wobei die niederwertigsten 6 Bits mit 0 aufgefüllt seien.

Wenn der Messbereich von 0 bis 2.56000 Volt reicht, dann lässt sich die Spannung so anzeigen:

 out_uwfxdp( w, 3, 256, 2);

Hierbei ist

  • w : der Wert aus dem ADC, vorzeichenlos, linksbündig
  • 3 : die gewünschte Zahl der Nachkommastellen
  • 256 : Integerdarstellung von 2.56
  • 2: weil die Kommastelle von 2.56 um 2 Stellen nach rechts geschoben wurde und als 256 dargestellt wird.

x = 6554 (das ist 1/10 von 65535) wird zu: 6554*256/65536=25.6015625, die Nachkommastelle um 2 verschoben: 0.256015625, gerundet auf 3 Stellen: 0.256, das ist 1/10 von 2.56

Beispiel 2

Es sei eine Zahl w in 16 Bit dargestellt. Diese Zahl reiche 0 bis 65535 Dies seien z.B: 16 Bit von einem 16-Bit-ADC (ohne Vorzeichen). Oder von einem 10-Bit ADC, wobei die niederwertigsten 6 Bits mit 0 aufgefüllt seien.

Wenn der Messbereich von 0 bis 155.67 Grad Celsius reicht, dann lässt sich die Temperatur so anzeigen:

out_uwfxdp( w, 3, 15567, 2);

Hierbei ist:

  • w : der Wert aus dem ADC, vorzeichenlos, linksbündig
  • 3 : die gewünschte Zahl der Nachkommastellen
  • 15567 : Integerdarstellung von 155.67
  • 2: weil die Kommastelle von 155.67 um 2 Stellen nach rechts geschoben wurde und als 15567 dargestellt wird.

x = 6554 (das ist 1/10 von 65535) würde werden als:

6554*15567/65536=1556.7950, die Nachkommastelle um 2 verschoben: 15.567950, gerundet auf 3 Stellen: 15.568.

Quellen

 // Das ist in Quelle [[Muluwuw muluwuw.cpp]]

 #if !defined(__AVR_HAVE_MUL__) /* Software- Multiply */
 // input:
 // r23_r22 = x.h_x.l
 // r25_r24 = y.h_y.l
 // may destroy r18 r19 r20 r21 r22 r23 r24* r25* r26* r27*
 // may not destroy r14 r15 r16 r17 r28=YL r29=YH
 // return r25_r24_r23_r22


 FUNCT u32 dummy_mul_uw_uw( u16 x , u16 y )
 {

 // asm(" .text " "n");
 asm(" ; " "n");
 asm("  .global mul_uw_uw" "n");
 asm(" ; See as well mul_uc_uc : 8 bit by 8 bit, result 16 bits " "n");
 asm(" ; " "n");
 asm(" ; Function mul_uw_uw " "n");
 asm(" ; multiply unsigned 16 bit by 16 bit " "n");
 asm(" ; " "n");
 asm(" ; Input: " "n");
 asm(" ;     r23:r22   : The factor x " "n");
 asm(" ;     r25:r24   : The factor y " "n");
 asm(" ; Results " "n");
 asm(" ;     r25:r24:r23:r22    : result " "n");
 asm(" ; Destroyed registers " "n");
 asm(" ;     none " "n");
 asm(" ; This program is verified by qqarith.asm " "n");
 asm(" ; " "n");
 //asm(" _Z9mul_uw_uwtt: " "n");
 asm("mul_uw_uw: " "n");
 asm("     push r16 ; " "n");
 asm("     push r18 ; " "n");
 asm("     push r19 ; " "n");
 asm("     push r20 ; " "n");
 asm("     push r21 ; " "n");
 asm("     push r26 ; " "n");
 asm("     push r27 ; " "n");
 asm(" ; Clear start values " "n");
 #ifdef __AVR_HAVE_MOVW__
 asm("     movw r26,r22 ; factor x to interim storage " "n");
 asm("     movw r18,r24 ; factor y to interim storage , extend" "n");
 #else
 asm("     mov r26,r22 ; factor x to interim storage " "n");
 asm("     mov r27,r23 " "n");
 asm("     mov r18,r24 ; factor y to interim storage , extend" "n");
 asm("     mov r19,r25 " "n");
 #endif
 asm("     clr r20 " "n");
 asm("     clr r21 " "n");
 asm(" ; Clear result r25:r24;r23;r22 " "n");
 asm("     clr r22 " "n");
 asm("     clr r23 " "n");
 asm("     clr r24 " "n");
 asm("     clr r25 " "n");
 asm(" ; " "n");
 asm(" mul_uw_uw_3: " "n");
 asm("     lsr    r27 ; shift x" "n");
 asm("     ror    r26        ; next binary digit of right factor to carry " "n");
 asm("     brcc    mul_uw_uw_4 " "n");
 asm(" ; Add the (shifted) left factor into result " "n");
 asm("     add    r22,r18 " "n");
 asm("     adc    r23,r19 " "n");
 asm("     adc    r24,r20 ; add 0" "n");
 asm("     adc    r25,r21 ; add 0" "n");
 asm(" mul_uw_uw_4: " "n");
 asm(" ; shift the factor y by 1 bit to left" "n");
 asm("     add    r18,r18 " "n");
 asm("     adc    r19,r19 " "n");
 asm("     adc    r20,r20 " "n");
 asm("     adc    r21,r21 " "n");
 asm("     mov    r16,r26        ; no more of right factor left " "n");
 asm("     or    r16,r27 " "n");
 asm("     brne mul_uw_uw_3 " "n");
 asm(" ; result is 32 bits in r25:r24:r23:r22 " "n");
 asm("     pop    r27 " "n");
 asm("     pop    r26 " "n");
 asm("     pop    r21 " "n");
 asm("     pop    r20 " "n");
 asm("     pop    r19 " "n");
 asm("     pop    r18 " "n");
 asm("     pop    r16 " "n");
 // asm("     ret " "n");


 // The warning here is intentionally expected. a dummy return would waste 8 words
 // return 0 ; 
 }

 #else

 // Hardware multiply MUL
 // input:
 // r23_r22 = x.h_x.l
 // r25_r24 = y.h_y.l
 // may destroy r18 r19 r20 r21 r22 r23 r24* r25* r26* r27*
 // may not destroy r14 r15 r16 r17 r28=YL r29=YH
 // return r25_r24_r23_r22


 FUNCT u32 dummy_mul_uw_uw( u16 x , u16 y )
 {
 //asm(" .text " "n");
 asm(" .global mul_uw_uw   ; The entry point" "n");
 asm(" ; See as well mul_uc_uc : 8 bit by 8 bit, result 16 bits " "n");
 asm(" ; " "n");
 asm(" ; Function mul_uw_uw " "n");
 asm(" ; multiply unsigned 16 bit by 16 bit " "n");
 asm(" ; " "n");
 asm(" ; Input: " "n");
 asm(" ;     r23:r22   : The factor " "n");
 asm(" ;     r25:r24   : The factor " "n");
 asm(" ; Results " "n");
 asm(" ;     r25:r24:r23:r22    : result" "n");
 asm(" ; r1 = 0 (as it is GCC standard)" "n" );
 asm(" ; Destroyed registers " "n");
 asm(" ;     ro (as allows GCC standard) " "n");
 asm(" ; This program is verified by qqarith.asm " "n");
 asm(" ; " "n");
 //asm(" _Z9mul_uw_uwtt: " "n");
 asm("mul_uw_uw: " "n");
 asm("     push r18 ; " "n");
 asm("     push r19 ; " "n");
 asm("     push r20 ; " "n");
 asm("     push r21 ; " "n");
 #ifdef __AVR_HAVE_MOVW__
 asm("     movw r20,r22 ; factor x to interim storage " "n");
 asm("     movw r18,r24 ; factor y to interim storage " "n");
 #else
 asm("     mov r20,r22 ; factor x to interim storage " "n");
 asm("     mov    r21,r23 " "n");
 asm("     mov r18,r24 ; factor y to interim storage " "n");
 asm("     mov    r19,r25 " "n");
 #endif

 asm("     mul    r20,r18 ; low byte * low byte" "n" );
 #ifdef __AVR_HAVE_MOVW__
 asm("     movw    r22,r0 ; low result word" "n" );
 #else
 asm("     mov    r22,r0 ; low result word" "n" );
 asm("     mov    r23,r1 ; low result word" "n" );
 #endif
 asm("     mul    r21,r19 ; high byte * high byte" "n" );
 #ifdef __AVR_HAVE_MOVW__
 asm("     movw    r24,r0 ; high result word" "n" );
 #else
 asm("     mov    r24,r0 ; high result word" "n" );
 asm("     mov    r25,r1 ; high result word" "n" );
 #endif
 asm("     mul    r20,r19 ; low byte * high byte" "n" );
 asm("     add    r23,r0 ; medium result word" "n" );
 asm("     adc    r24,r1 ; medium result word" "n" );
 asm("     clr    r1 ; does not modify Carry flag" "n" );
 asm("     adc    r25,r1 ; carry to highest byte" "n" );
 asm("     mul    r21,r18 ; low byte * high byte" "n" );
 asm("     add    r23,r0 ; medium result word" "n" );
 asm("     adc    r24,r1 ; medium result word" "n" );
 asm("     clr    r1 ; does not modify Carry flag" "n" );
 asm("     adc    r25,r1 ; carry to highest byte" "n" );
 asm(" ; result is 32 bits in r25:r24:r23:r22 " "n");
 asm("     pop    r21 " "n");
 asm("     pop    r20 " "n");
 asm("     pop    r19 " "n");
 asm("     pop    r18 " "n");
 // asm("     ret " "n");

 // The compiler warning here is intentionally expected. a dummy return would waste 8 words
 // return 0 ; 
 }

 #endif


 // Das ist in Quelle helq.h
 struct snums {
 u32 v ;
 u8 k ;
 u8 d ;
         } ;

 // das ist in Quelle vnums.cpp

 struct snums vnums[NNUMS] = {
     { 4000000000UL, 0, 4 } ,        // 0
     { 2000000000UL, 0, 2 } ,
     { 1000000000UL, 0, 1 } ,
     {  800000000UL, 1, 8 } ,
     {  400000000UL, 1, 4 } ,
     {  200000000UL, 1, 2 } ,
     {  100000000UL, 1, 1 } ,
     {   80000000UL, 2, 8 } ,
     {   40000000UL, 2, 4 } ,
     {   20000000UL, 2, 2 } ,
     {   10000000UL, 2, 1 } ,        // 10
     {    8000000UL, 3, 8 } ,
     {    4000000UL, 3, 4 } ,
     {    2000000UL, 3, 2 } ,
     {    1000000UL, 3, 1 } ,        // 14
     {     800000UL, 4, 8 } ,        // 15
     {     400000UL, 4, 4 } ,        // 16
     {     200000UL, 4, 2 } ,
     {     100000UL, 4, 1 } ,
     {      80000UL, 5, 8 } ,
     {      40000UL, 5, 4 } ,        // 20
     {      20000UL, 5, 2 } ,
     {      10000UL, 5, 1 } ,
     {       8000UL, 6, 8 } ,
     {       4000UL, 6, 4 } ,
     {       2000UL, 6, 2 } ,        // 25
     {       1000UL, 6, 1 } ,
     {        800UL, 7, 8 } ,
     {        400UL, 7, 4 } ,
     {        200UL, 7, 2 } ,        // 29
     {        100UL, 7, 1 } ,        // 30
     {         80UL, 8, 8 } ,
     {         40UL, 8, 4 } ,
     {         20UL, 8, 2 } ,
     {         10UL, 8, 1 } ,
     {          8UL, 9, 8 } ,        // 35
     {          4UL, 9, 4 } ,
     {          2UL, 9, 2 } ,
     {          1UL, 9, 1 } } ;    // 38

 // Das ist in Quelle otu32dp.cpp
 #define MAXDIGITS 10
 FUNCT void out_uint32_dp( 
     u32 h,      // Auszugebende Zahl 
     u8 dp)      // Anzahl der nachkommastellen
 {
 char dig[MAXDIGITS] ;
 u8 i ;
 u8 s ;        // Start Digit



 for ( i = 0 ; i < MAXDIGITS ; ++i )
     {
     dig[i] = '0' ;    // i = 0 ... 9 : 10 digits
     }
 // optimize for small numbers
 if ( h < 80000L )
     {                 // F&uuml;r 16-bit-Zahlen brauchen wir nicht die ganze Tabelle vnums durcharbeiten
     if ( h < 400L )
         {        // 8 bit numbers are below 256
         i = 29 ;    // start at Table entry 29: there is 200
         s = 7 ;
         }
     else
         {        // 16 bit numbers are below 65536
         i = 20 ;    // start at Table entry 16, there is 40000 
         s = 5 ;
         }
     }
 else
     {    // Big numbers
     i = 0 ;
     s = 0 ;
     }

 for ( i = i ; i < NNUMS ; ++i )
     {
     if ( h >= vnums[i].v )
         {
         h -=  vnums[i].v ;
         dig[vnums[i].k] += vnums[i].d ;
         }
     }
 // Skip 0 to 8 leading zeroes place(10^10 to 10^1)
 // but keep always the least 0

 if ( MAXDIGITS-dp < s ) s = MAXDIGITS-dp ;
 for ( i = s ; i < MAXDIGITS-1 ; ++i )
     {
     if ( i == MAXDIGITS-dp ) 
         {
         out_char( '0' );
 //        out_char( '.' );
         break ;
         }
     if ( dig[i] != '0' ) break ;
     }

 // Display rest of the ASCII converted number
 for ( i = i ;  ; ++i )
     {
     if ( i == MAXDIGITS-dp ) 
         {
         out_char( '.' );
         }
     if ( i >= MAXDIGITS ) break ;
     out_char( dig[i] );
     }
 }


 // Das ist in Quelle round325.cpp
 u32 round325[5] =
     {
     32768L,    // 65536L/2/1 ,        // 32768 = 0x8000 will be interpreted as 0.500000
     3276L,    // 65536L/2/10 ,        // 3276
     327L,    // 65536L/2/100 ,        // 327
     32L,    // 65536L/2/1000 ,        // 32
     3L    // 65536L/2/10000        // 3
     } ;

 FUNCT void out_uwfxdp(
         u16 w,            // The fixed point part, left justified, 0xffff = 0.999999, 0x8000 = 0.500
         u8 prec,        // How many decimales to display after the decimale point
         u16 max_w,        // The value which is w=0xffff + 1
         u8 dp)            // Decimales in max_w
 {
 u32 ul ;
 u16 teil ;
 u16 x ;
 u8 i ;

 ul = mul_uw_uw(w, max_w); // 437*1200=524400
 if ( prec < dp ) prec=dp ;
 if ( prec-dp < 5 )
     {
     ul += round325[prec-dp] ; // Rundungstabelle 
     }
 x = (u16)(ul >> 16) ;
 teil = (u16)ul ;
 out_uint32_dp(x, dp); // Ausgabe des Integer-Teils mit Dezimalpunkt

 // Ausgabe der Nachkommastellen
 for ( i = 0 ; i < prec-dp ; ++i )
     {
     ul = mul_uw_uw(teil, 10);   // Rechne: ul = teil * 10
     x = (u16)(ul >> 16) ;    // overflow bits
     out_char( x + '0' );    // output
     teil = (u16)ul;         // The overflow bits have been output, but teil not yet.
     }
 }

Ausgabe der Nachkommastellen

Das Ergebnis von teil*10 ist eine Zahl mit 20 Bits:
teil, 16 bit
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 t t t t t t t t t t t t t t t t
Ergebnis: ul, 32 bit
0 0 0 0 0 0 0 0 0 0 0 0 x x x x y y y y y y y y y y y y y y y y

Davon sind die ersten 4 Bits x von 0 bis 9, mehr ist nicht möglich

Beispiel : 60000 * 10 = 600000,

hex: EA60 * A = 927C0, also wird 9 ausgegeben und mit 27C0 weitergerechnet.
hex: 27C0 * A = 18D80, also wird 1 ausgegeben und mit 8d80 weitergerechnet.
hex: 8D80 * A = 58700, also wird 5 ausgegeben und mit 8700 weitergerechnet.
hex: 8700 * A = 54600, also wird 5 ausgegeben und mit 4600 weitergerechnet, usw.

60000/65536=0.91552734375: Diese Nachkommastellen entstehen nach und nach durch fortlaufendes Multiplizieren.

Siehe auch: Muluwuw