Forum: Compiler & IDEs Wie float in integer wandlen mit Runden?


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Mike (Gast)


Lesenswert?

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?

von Andreas B. (bitverdreher)


Lesenswert?

0.5 vorher zu den float addieren,

von Dirk B. (dirkb2)


Lesenswert?

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.

von abc (Gast)


Lesenswert?

Dazu gibt es die library Funktionen: floor (abrunden) und ceil 
(aufrunden)

von abc (Gast)


Lesenswert?

Und round ( normales runden) gibt es auch in der c Standard library

von Andreas B. (bitverdreher)


Lesenswert?

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.

von Bauform B. (bauformb)


Lesenswert?

round() liefert blöderweise wieder ein double.
lround() liefert auch kein int, aber immerhin long

von WF88 (Gast)


Lesenswert?

Bauform B. schrieb:
> round()

Und was hindert dich daran, das Ergebnis von round() zu casten?

von Ingo Less (Gast)


Lesenswert?

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
}

von Wolfgang (Gast)


Lesenswert?

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.

von Wolfgang (Gast)


Lesenswert?

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 ;-)

von A. S. (achs)


Lesenswert?

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.

von Bert (Gast)


Lesenswert?

Kann man nicht das Vorzeichen Bit zum multiplizieren verwenden, um die 
Fallunterscheidung zum machen?

von A. S. (achs)


Lesenswert?

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
}

von Dirk B. (dirkb2)


Lesenswert?

A. S. schrieb:
>  return (int) (f+f>0?0,5f:-0,5f);

Der Kommaoperator ist hier falsch.

von A. S. (achs)


Lesenswert?

Dirk B. schrieb:
> Der Kommaoperator ist hier falsch.

Ups. Ja, bitte 0.5 statt 0,5

von Klaus H. (klummel69)


Lesenswert?

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
}

von Rolf M. (rmagnus)


Lesenswert?

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
}

von Bauform B. (bauformb)


Lesenswert?

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... :(

von Rolf M. (rmagnus)


Lesenswert?

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 ;-)

von Schadkot (Gast)


Lesenswert?

Was spricht gegen (int)roundf(x) ?

von rbx (Gast)


Lesenswert?

Nur mal so gefragt: hat das Ding keinen "rounding mode", den man in der 
FPU einstellen kann?

von Bauform B. (bauformb)


Lesenswert?

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... :(

von Rolf M. (rmagnus)


Lesenswert?

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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.