2012-04-02 11 views
1

Im Moment habe ich wie folgt mache:wie kann ich erhöhen Genauigkeit ohne Schwimmer C

uint8_t ManualFlow = 40; // 0 -> 255  Unsigned Char 

uint24_t ME; // 0 -> 16777215 Unsigned Short Long 
ME = (uint24_t) ManualFlow*10; // Have to make this hack otherwise malfunction in calculation 
ME /= 6; 
ME *= (80 - 60); 
ME /= 100; 
ME *= 414; 

Das Endergebnis:

40*10 = 400 
400/6 = 66 
66*20 = 1320 
1320/100 = 13 
13*414 = 5382 

Was ich liebe würde, ist ähnlich wie diese:

4/60 = 0,0667 * 20 * 4188 * 0,998 = 5576 (more accurate). 

Wie kann ich das genauer ohne Verwendung von float s oder double s, und am wichtigsten nicht meine Code-Größe zu sehr erhöhen.

Mit freundlichen Grüßen Sonite

+0

Da ich ein uint24_t habe, werde ich ManualFlow * 10 auf Manualflow * 100 erhöhen. Das wird es viel genauer machen. Größte Werte, die ich haben kann sind: "1000" * 10/6 * "100"/100 * 414 = 689724 – Christian

+0

Sie verschwenden Bits dort ... Wenn Sie ein uint8_t und uint24_t haben, bedeutet es, dass Sie nach links verschieben können uint8_t um 16 Bits. Das ist ~ 600-mal genauer als die Multiplikation mit 100 und auch hier ist es viel schneller, Multiplikation ist im Vergleich zu Bit-Verschiebungen sehr teuer. –

Antwort

0

Schlussfolgerung:

Meine anfängliche Code hatte nicht so eine gute Genauigkeit und war "das große".

Damit unter ich tun Sie den Code mit „38 Byte“ erhöht und gewann eine bessere Genauigkeit

ME = (uint24_t) ManualFlow*100; 
ME /= 6; 
ME *= (Port[2].AD - Port[3].AD); 
ME /= 100; 
ME *= 414; 
ME /= 10; 

Die beste Genauigkeit ich tun fester Punkt bekam, aber es erhöht den Code zu viel „1148 Bytes ->

// Utility macros for dealing with 16:16 fixed-point numbers 
#define I2X(v) ((int32_t) ((v) * 65536.0 + 0.5)) // Int to Fix32 
#define X2I(v) ((int16_t) ((v) + 0x8000 >> 16))  // Fix to Int 

ME = I2X(ManualFlow*10); //400 * 65536.0 + 0.5 = 26214400 
ME = I2X(ME/6); // 26214400/6 = 4369066 
ME = I2X(ME * 20); // = 87381320 
ME = I2X(ME/100); // = 873813 
ME = I2X(ME * 414); // 361758582 
ME = X2I(ME); // 158FFF76 + 8000 >> 16 15907F76 >> 16 = 5520 

Hoffe, es kann jemand anderem helfen!

Mit freundlichen Grüßen Sonite

0

Multiply alle Eingaben z.B. (1 8) in größere Datentypen, führen Sie dann die erforderlichen Berechnungen durch, und teilen Sie die Antwort durch (1 < < 8).

1

Wenn Sie sicher sind, dass das Ergebnis Überlauf nie, bevor die Divisionen alle Ihre Vermehrung tun:

uint24_t ME; 
ME = (uint24_t)ManualFlow*10; 
ME *= (80 - 60); 
ME *= 414; 
ME /= (6 * 14); 

Wenn Sie mehr als integer Genauigkeit benötigen, wollen aber Gleitkommazahlen vermeiden, sollten Sie fixed-point arithmetic mit stattdessen.

+0

Ah ... das ist ein enger. Der größte Wert, den ich hier haben könnte, ist dann: 400 * (100) * 414/(6 * 14) = 16560000. Wie du sagst, musst du sicher sein, dass es nicht überläuft. Ich denke, ich werde nach einer anderen Annäherung gehen. – Christian

1

Sie beziehen sich auf Festkomma-Arithmetik (im Gegensatz zu Fließkomma). Sie können die Größe Ihrer Ganzzahlen immer auf etwas wie uint64_t erhöhen und mit mehr als 10 multiplizieren, um die gewünschte Genauigkeit zu erreichen.

Ich würde jedoch empfehlen, base-2 Fixpunkt zu verwenden (d. H. Nach links um eine bestimmte Anzahl von Bits verschieben, anstatt mit 10 zu einer bestimmten Potenz zu multiplizieren). Es ist (viel) schneller und es kann genauer sein.

0

kann ich ein wenig zu spät sein für das ursprüngliche Plakat zu beantworten, aber für die Nachwelt es sollte auch darauf hingewiesen werden, dass Festpunktteilungen können oft vermieden werden, wenn die Geschwindigkeit auf einem wirklich kritisch ist kleiner Prozessor. Divisionen durch Variablen sind oft unvermeidlich, aber man kann (fast) immer eine Multiplikation und eine Verschiebung anstelle einer Division durch eine Konstante verwenden, die viele Prozessorzyklen auffressen kann, insbesondere für Typen, die größer sind als die Datenbreite eines kleinen Prozessors. Statt

uint16_t x = somevalue; //somevalue known to be between 0 and 65535 
x /= 107; 

können Sie verwenden:

uint32_t x = somevalue; 
x *= 39199; //chosen to be 2^n/107 
      //n chosen to maximum value without exceeding 65536 
x >>= 22; //n = 22 in this case 

Hinweis: Dies ist weniger lesbarer Code, aber wenn dies ein Algorithmus ist, wo die Leistung entscheidend ist, kann diese Optimierung verwendet werden (sparsam).

Verwandte Themen