Forum: PC-Programmierung Wie berechnung Formel anpassen


von Chandler B. (chandler)


Lesenswert?

Einen schönen guten morgen,
ich habe eine Formel in der ein Argument negativ werden kann.
1
uint16_t calcLength(uint16_t y)
2
{
3
  uint16_t res;
4
  uint16_t x;
5
  x = 18;
6
7
  res = 200 + (((((69 - x) * 1000) / 46) * (y - 200)) / 1000);
8
  return res;
9
}
y kann jetzt aber auch kleiner 200 werden, womit im momentanen Zustand 
die Gleichung was (für mich) falsches rausgibt (35318). Das ergebnis ist 
schon richtig (also was berechnet wird), allerdings nur weil es die 
Formel so besagt. Bekommen möchte ich den wert 192.

Jetzt gibt es zwei möglichkeiten.
Entweder ich mache ein if/else
1
if(200 <= y) res = 200 + (((((69 - x) * 1000) / 46) * (y - 200)) / 1000);
2
else res = 200 - (((((69 - x) * 1000) / 46) * (200 - y)) / 1000);

oder mit casten
1
res = (uint16_t)(200 + (((((69 - (int16_t)x) * 1000) / 46) * ((int16_t)y - 200)) / 1000));

Welche Möglichkeit ist besser (professioneller)?
Letztendlich kommt bei beiden das selbe raus. ersteres ist einfacher zu 
lesen. Wobei die Formel (so wie sie momentan steht) auch so im 
Datenblatt steht.
Bei der zweiten Möglichkeit ist die Formel so wie sie im Datenblatt 
steht, aber schwieriger zu lesen (meiner Meinung nach).

von g457 (Gast)


Lesenswert?

> Welche Möglichkeit ist besser [..] ?

Weder noch. y vorzeichenbehaftet machen und gut iss.

von Georg M. (g_m)


Lesenswert?

> * 1000 / 1000

Muss das sein?

von Chandler B. (chandler)


Lesenswert?

g457 schrieb:
> Weder noch. y vorzeichenbehaftet machen und gut iss.

Das ist aber leider so gegeben.

Georg M. schrieb:
>> * 1000 / 1000
>
> Muss das sein?

Ja, hintergrund ist, dass float nicht benutzt werden darf. x ist 
normalerweise eine Variable und liegt zwischen 5 und 35 (wird von 
woanders vorgegeben).
(69 - x) / 46) = 1,39
(69 - 35) / 46) = 0,73
sprich für alle werte kämen nur 1 oder 0 heraus, was die Berechnung 
falsch machen würde. Mit den *1000 und /1000 umgehe ich dies

von A. S. (Gast)


Lesenswert?

Wenn Du nicht casten willst oder y signed machen willst, dann weise y 
vorher einer signed-Variablen zu. Das ist geradliniger.

x zu casten ist bullshit in dem Code, setze es direkt signed. Und falls 
wirklich konstant, wozu das ganze?

Entweder hast Du noch ein grundlegendes Verständnisproblem oder der 
Beispiel-Code ist unbrauchbar, poste den original-Code.

Casten ist übel (der Compiler kann nicht mehr warnen), if hier genauso.

Und ohne Checks (asserts) wirst Du in der Praxis früher oder später in 
Überläufe rutschen.

Georg M. schrieb:
>> 1000 / 1000
>
> Muss das sein

Hier ging es Georg um Rechenleistung: wenn sowieso alles auf Kante 
genäht ist (int16 statt int32) warum dann nicht

 res = 200 + (((((69 - x) * 1024) / 46) * (y - 200)) / 1024);

Was sich verlustfrei kurzen lässt zu

 res = 200 + (((((69 - x) * 512) / 23) * (y - 200)) / 512);

Die echte Division wird dabei allerdings nur eingespart, wenn es 
unsigned ist. Also ggf schrittweise rechnen oder echten Code posten

von Georg M. (g_m)


Lesenswert?

200 + (((((69 - x) * 1000) / 46) * (y - 200)) / 1000) =

= (200x - xy + 69y)/46 - 100

von Der Opa aus der Muppet Show (Gast)


Lesenswert?

> Welche Möglichkeit ist besser (professioneller)?

Die professionellen Entwickler benutzen 32 Bit Mikrocontroller mit 
unendlich Hauptspeicher. Da nimmt man einfach einen Datentyp, bei dem 
man sich über so etwas keine Gedanken machen muss.

Damit will ich nicht behaupten, mit irrsinnig leistungsfähiger Hardware 
Entwicklungszeit einsparen wäre besser. Es ist halt professioneller.

von Einer (Gast)


Lesenswert?

Chandler B. schrieb:
> x = 18;
>   res = 200 + (((((69 - x) * 1000) / 46) * (y - 200)) / 1000);

Naja, man kann ja vereinfachen...

Erster Schritt, die Konstante einsetzen und fertig rechnen:

> res = 200 + (((((69 - 18) * 1000) / 46) * (y - 200)) / 1000);

ergibt:

> res = 200 + (((51000 / 46) * (y - 200)) / 1000);

Die erste Division (/46) wird, wegen der Klammerung und der Sprache C, 
abgeschnitten. Effektiv steht dann da:

> res = 200 + ((1108 * (y - 200)) / 1000);

Das ist das, was wirklich gerechnet wird. Jetzt verlassen wir die 
Sprache C, und vereinfachen mathematisch korrekt, indem wir 
ausmultiplizieren und durch die 1000 dividieren. Dann bleibt:

> res = 200 + 1.108 * y - 221.6;

Sodele. Ziel ist, alles unsigned zu rechnen. Also muss das Minus von der 
Kommazahl weg. Dazu zählen wir 222 zu 221.6 dazu, und ziehen 222 von 200 
ab:

> res = -22 + 1.108 * y + 0.4;

Da können wir schon mal einen Batzen weg nehmen:

> res = -22 + y + 0.108 * y + 0.4;

Faktor 1000

> res = -22 + y + (108 * y + 400) / 1000;

Wie man sofort sieht, kann man mit 4 kürzen:

> res = -22 + y + (27 * y + 100) / 250;

That's it.

Diese Berechnung benötigt nur eine Addition mehr (sehr schnell), kann 
Eingangswerte bis einschließlich 2423 verarbeiten (Original nur 259), 
und benötigt keine Fall-Unterscheidung für y-Werte unter 200.

von Tilo R. (joey5337) Benutzerseite


Lesenswert?

Wenn ich die Formel umstelle kommt am Ende raus:
1
res = 200 + ((69 - x) * (y - 200)) / 46
Du schreibst float soll nicht verwendet werden. Daraus schließe ich, es 
soll prinzipiell schon genau sein. Die Multiplikation/Division mit 1000 
soll keine implizite Rundung erzeugen, sondern nur ein bischen vor 
Genauigkeitsverlust schützen.

Generell gilt wenn man mit Integern rechnet: So lange der Datentyp groß 
genug ist, zuerst alle Multiplikationen, erst am Schluss 
kleinerdividieren. Dann dividiert man auch die vorher ggf. gemachten 
Rundungsfehler klein.

Zur Vorzeichenproblematik würde ich auch einfach auf signed gehen. (Aber 
prüfen, ob dann der Wertebereich für y noch reicht.)

Die Mühe, die Konstante x von Hand in die Formel reinzuziehen, würde ich 
mir definitiv nicht machen. Das kann der compiler genau so gut.

Guter Stil wäre es aber imho, im Code auch zu schreiben, dass x eine 
Konstante ist:
1
const uint16_t x = 18;

In manchen Fällen hilft const auch dem Compiler. Es dokumentiert aber 
auf jeden Fall die Intention des Programmierers. Und erzeugt Warnungen, 
wenn man sie doch irgendwo versehentlich ändert.
Ich würde auch y in der Methodensignatur const machen, gebe aber zu dass 
man da anderer Meinung sein kann.

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
Noch kein Account? Hier anmelden.