Ich habe ein digitales Filter auf einem STM32F411 programmiert, das intern mit Gleitkommazahlen rechnet. Nun muss ich diese zwecks Ausgabe auf einem AD-Wandler wieder in 16bit-Integers wandeln. Dabei soll zur nächstliegenden Ganzzahl gerundet werden. Der einfache cast (int16_t) schneidet die Nachkommastellen hingegen einfach ab. Wie macht man das am elegantesten ohne unschöne Fallunterscheidung?
Andreas B. schrieb: > 0.5 vorher zu den float addieren, Da muss man aber auf das Vorzeichen achten. -1.9 soll auf -2 gerundet werden.
Dazu gibt es die library Funktionen: floor (abrunden) und ceil (aufrunden)
Und round ( normales runden) gibt es auch in der c Standard library
Dirk B. schrieb: > Da muss man aber auf das Vorzeichen achten. Stimmt. Allerdings bin ich bei einem AD Wandler davon ausgegangen daß es sich nur um positive Werte handeln kann.
round() liefert blöderweise wieder ein double. lround() liefert auch kein int, aber immerhin long
1 | if ( ((int16_t)(var_float * 10)) % 10 >= 5){ //aufrunden |
2 | var_int = (int16_t) (var_float + 0.5f); |
3 | }else{ //abrunden |
4 | var_int = (int16_t) var_float; |
5 | }
|
Andreas B. schrieb: > Stimmt. Allerdings bin ich bei einem AD Wandler davon ausgegangen daß es > sich nur um positive Werte handeln kann. Das kommt auf den AD-Wandler und das von ihm verlangte Datenformat (Zweierkomplement bzw. Linear mit Offset) an.
Mike schrieb: > Nun muss ich diese zwecks Ausgabe > auf einem AD-Wandler wieder in 16bit-Integers wandeln. p.s. Zur Ausgabe würde ich es an deiner Stelle besser mit einem DA-Wandler probieren ;-)
Ingo Less schrieb: > (int16_t)(var_float * 10)) % 10 > % bei negativen Zahlen ist nicht wirklich intuitiv. Jedenfalls ist es hier Glücksache (i.d.), ob das funktioniert.
Kann man nicht das Vorzeichen Bit zum multiplizieren verwenden, um die Fallunterscheidung zum machen?
Bert schrieb: > Kann man nicht das Vorzeichen Bit zum multiplizieren verwenden, um die > Fallunterscheidung zum machen? Warum so kompliziert (statt straight)?
1 | int runden(float f) |
2 | {
|
3 | int i; |
4 | |
5 | if(f >= 0) i = (int) (f+0,5f); |
6 | else i = (int) (f-0,5f); |
7 | return i; |
8 | }
|
9 | |
10 | // oder in einer Zeile
|
11 | |
12 | int runden(float f) |
13 | {
|
14 | return (int) (f+f>0?0,5f:-0,5f); |
15 | }
|
A. S. schrieb: > return (int) (f+f>0?0,5f:-0,5f); ein schönes Beispiel das man für Anfänger nicht schnell Beispielcode aus der Hand reinwirft: 1. float nutzt Dezimalpunkt "." nicht Komma "," 2. der ?: Operator hat niedrigere Priorität als der + Operator. Ergebnis: x = runden(3.5); ergibt 0. So wäre es richtig
1 | int runden(float f) |
2 | {
|
3 | return (int) (f+(f>0?0.5f:-0.5f)); |
4 | }
|
Da finde ich es aber so lesbarer:
1 | int runden(float f) |
2 | {
|
3 | return (int) f>0?f+0.5f:f-0.5f; |
4 | }
|
Wobei ich normalerweise ein paar mehr Leerzeichen verteilen würde. Man kann auch das Vorzeichen übertragen:
1 | int runden(float f) |
2 | {
|
3 | return (int) (f + copysignf(0.5f, f); |
4 | }
|
Bauform B. schrieb: > round() liefert blöderweise wieder ein double. > lround() liefert auch kein int, aber immerhin long hier beim Cortex-M4 muss es natürlich lroundf() heißen
1 | long int lround(double x); |
2 | long int lroundf(float x); |
3 | long int lroundl(long double x); |
Eigentlich wollte ich nur die eure Versuche ins Lächerliche ziehen, round() neu zu erfinden. Das geht nun natürlich nicht mehr :( Aber im Ernst: wenn man schon sowas selbst baut, obwohl es seit C99 gratis dabei ist, sollte man doch mal schauen, ob der M4 nicht einen Maschinenbefehl dafür hat. Dann hätte man eine "static inline int runden (float)", die aus einem einzigen Maschinenbefehl besteht. VCVTR sieht gut aus, ich bin nur nicht sicher, ob "round to nearest" wirklich der Default ist. Der aktuelle Mode steht im FPSCR[23:22] und 0b00 wäre der richtige Wert. Aber es gibt noch das "Floating-point Default Status Control Register" FPDSCR und die Initialisierungen durch Compiler/Runtime/Framework/IDE/Whatever... :(
Bauform B. schrieb: > Eigentlich wollte ich nur die eure Versuche ins Lächerliche ziehen, > round() neu zu erfinden. Das geht nun natürlich nicht mehr :( Deshalb habe ich noch copysignf() reingebracht ;-)
Nur mal so gefragt: hat das Ding keinen "rounding mode", den man in der FPU einstellen kann?
rbx schrieb: > Nur mal so gefragt: hat das Ding keinen "rounding mode", den man in der > FPU einstellen kann? Bauform B. schrieb: > VCVTR sieht gut aus, ich bin nur nicht sicher, ob "round to nearest" > wirklich der Default ist. Der aktuelle Mode steht im FPSCR[23:22] und > 0b00 wäre der richtige Wert. Aber es gibt noch das "Floating-point > Default Status Control Register" FPDSCR und die Initialisierungen durch > Compiler/Runtime/Framework/IDE/Whatever... :(
rbx schrieb: > Nur mal so gefragt: hat das Ding keinen "rounding mode", den man in der > FPU einstellen kann? Das finde ich nicht empfehlenswert, denn das würde das Verhalten von float-Konvertierungen inkompatibel zu Standard-C machen. Es wird aber evtl. Bibliotheksfunktionen oder gar vom Compiler generierten Code geben, der dann fehlerhafte Ergebnisse produziert, weil er vom Standard-Verhalten ausgeht.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.