Festkommazahlen ausgeben
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ü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