Lineare Funktion implementieren

#7386016
Lesenswert?

Hallo,

ich stehe gerade auf dem schlauch eine funktion in c zu entwickeln. Die Sprache ist hierbei sogar noch egal.

ich habe zwei Punkte (-5|0)(25|100) und möchte hier eine lineare Funktion zu bilden um dazwischen die Werte zu ermitteln.

Jetzt sehe ich nur die Möglichkeit zunächst die steigung M zu berechnen, dann den Y-Achsenabschnitt B um damit die Gleichung zu bekommen, mit der ich den gewünschten Punkt berechnen kann.

1
uint8 m, b, y;
2
m = (100 / (25 - (-5)); // = 3,3333 (uint8) = 3
3
b = m * (-(-5)); // = 16,6666 (wegen uint8  = 15)
4
y = m * x + b;
5
return y;

das ganze würde funktionieren (wenn auch sehr ungenau, oder ich würde die Datentypen auf real abändern). Aber muss ich wirklich erst m und b berechnen? Oder gibt es da nicht eine einfachere Lösung zu?

#7386085
Lesenswert?

Auch wenn es aus der Arduinowelt kommt, finde ich das hier sehr nützlich:

int32_t map(int32_t x, int32_t in_min, int32_t in_max, int32_t out_min, int32_t out_max) { return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; }

bei dir wäre in_min = -5 in_max = 25 out_min = 0 out_max = 100 und x der Wert den du haben willst. Mit Integergenauigkeit beim Rechnen und ohne zwischenspeichern (mit Fließkomma!) von Steigung und Achsenabschnitt.

#7386252
Lesenswert?

Chandler B. schrieb:

Aber muss ich wirklich erst m und b berechnen? Oder gibt es da nicht eine einfachere Lösung zu?

Naja, würde man auf dem Papier ja auch so machen. Mit einem höheren Wertebereich könnte man durchaus genauer werden, ich sehe aber noch nicht, warum du dich überhaupt auf 8 bit festdübelst? Das würde man ja nicht mal auf einem Mikrocontroller machen, wenn man es entsprechend genau bräuchte und hier sind wir im PC-Forum ;)

Wenn der Rückgabewert zwingend uint8_t sein muss würde ich es so lösen:

1
uint8_t linearFunction(uint8_t x){
2
  return (uint8_t)((float)(0-100)/(-5-25) * (x + 5)));
3
}

Ich sehe halt noch nicht, warum man sich hier freiwillig auf uint8_t festgedübelt hat.

#7387089
Lesenswert?

Christoph S. schrieb:

return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;

Das ist ein idealer Rechenweg in integer. Ich möchte Runden hinzufügen:

((x-in_min)*(out_max-out_min)+(in_max+1-in_min)/2)/(in_max-in_min)+out_min;

und die üblichen dx (=in_max - in_min) und dy (out_max-out_min) einführen:

return ((x-in_min)*dy+(dx+1)/2)/dx)

Genauer geht es auch nicht mit float. Allerdings kann es überlaufen, wenn der halbe Wertebereich oder mehr verwendet wird (es ist safe, wenn keine Differenz > 127 und dx > 0) Bei beliebigen Vorzeichen halt jeweils abs()

Moderator #7387138
Lesenswert?

Bruno V. schrieb:

((x-in_min)*(out_max-out_min)+(in_max+1-in_min)/2)/(in_max-in_min)+out_min;

Wozu ist das "+1" in der Mitte des Ausdrucks? Das gehört doch weg, oder?

Also so:

((x-in_min)*(out_max-out_min)+(in_max-in_min)/2)/(in_max-in_min)+out_min;

Beispiel, wo der Unterschied zum Tragen kommt:

out_max-out_min=8, in_max-in_min=7 und x-in_min=3

#7387413
Lesenswert?

Ich würde sogar soweit gehen zu behaupten, dass es, wenn man es richtig macht, mit Integren gar keinen Fehler gibt. Der Wert der rein geht, und der Wert der raus kommt sind diskret. Man bekommt also hier für jedes x genau ein y. Bei einer Linie wie hier, muss man also zwangsweise festlegem, was als auf der Linie zählt. Abrunden, Aufrunden, etc. kann man hier nur mir Integern immer exakt machen. Legt man anhand dessen fest, was auf der Gerade liegt, kann man das also auch nur mit integren exakt abbilden, und hat dort wirklich 0 Fehler.

Jenachdem, was man hier vor hat, ist es aber eventuell etwas unglücklich, nur x als Input zu betrachten. Denn die Funktion gibt ja nur einen Punkt für jedes x zurück, und wenn Δx < Δy, muss es an manchen x eigentlich mehr punkte auf der Linie geben als nur einen.

Da gibt es aber viele Wege darum herum. Man kann z.B. ein halboffenes Intervall aus 2 y werten machen, einen für das Minimum, einen für das Maximum. Oder man kann einen anderen Eingabebereich nehmen, 0<=t<Δx*Δy statt nur Δx<Δy, so dass man die Funktion für x und y als input verwenden könnte (indem man diese zuerst mit Δy bzw. Δx multipliziert).
Oder falls man nur über jeden Punkt einmal drüber will, könnte man 2 Funktionen machen, eine für x, eine für y, und dann abhängig davon, ob Δx<Δy entweder die eine oder die andere nehmen.

#7387427
Lesenswert?

Ein 32 bit Integer hat eine bessere Aufloesung wie ein 32bit float. Denn der 32bit integer kann -2^31 .. +2^31 darstellen, was 9 signifikanten Stellen plus vorzeichen entspricht. Der 32bit float hat 24 bit Mantisse was 6 signifikanten Stellen entspricht. Der Rest ist Exponent mit Vorzeichen. Der 32bit float hat einen groessen dynamischen Bereich. Allerdings fliegt die Genauigkeit bei einer Subtraktion zum Fenster raus. Fuer uebliche Geschichten komme ich mit 32bit integer durch. Allerdings bin ich auch sattelfest mit Mathematik und was schiefgehen kann mit Mathematik zusammen mit Informatik

#7387440
Lesenswert?

Daniel A. schrieb:

Ich würde sogar soweit gehen zu behaupten, dass es, wenn man es richtig macht, mit Integren gar keinen Fehler gibt. Der Wert der rein geht, und der Wert der raus kommt sind diskret. Man bekommt also hier für jedes x genau ein y. Bei einer Linie wie hier, muss man also zwangsweise festlegem, was als auf der Linie zählt.

Ja, aber das ist doch gerade der Fehler, den man zwangsläufig macht. Die Werte können nicht exakt auf der Linie liegen, weil sie nicht darstellbar sind, also muss man sich eben am Raster der möglichen Werte ausrichten. 5/2 ist in Integer eben 3. Wenn ich das wieder mit 2 multipliziere, kommt ich nicht wieder bei 5 raus, weil ich einen Rundungsfehler gemacht habe.

Abrunden, Aufrunden, etc. kann man hier nur mir Integern immer exakt machen.

Was verstehst du unter "exakt" im Zusammenhang mit Rundung? Runden ist etwas, das man tut, weil es nicht exakt geht oder zu aufwändig wäre.

Legt man anhand dessen fest, was auf der Gerade liegt, kann man das also auch nur mit integren exakt abbilden, und hat dort wirklich 0 Fehler.

Was auf der Geraden liegt, ergibt sich mathematisch. Man kann sich natürlich auch selber was davon abweichendes als korrekt definieren, wodurch dann der Fehler natürlich per Definition 0 wird, aber man hat eben trotzdem eine Abweichung von einer echten Geraden. Oder auf das obige Beispiel übertragen: Wenn ich definiere, dass 3 das korrekte Ergebnis für die Division 5/2 ist, dann führt das noch lange nicht dazu, dass die 3 genau in der Mitte zwischen 0 und 5 liegt.

#7387481
Lesenswert?

Daniel A. schrieb:

Ich würde sogar soweit gehen zu behaupten, dass es, wenn man es richtig macht, mit Integren gar keinen Fehler gibt.

Das ist ja m.E. bei Christophs Rechenweg so, auch wenn es mit fehlender/falscher Rundung 2 Iterationen bedurfte.

Die Float-Implementierung ist da häufig schneller hingeschrieben, da man sich da "keine Gedanken" machen muss. Am Ende ist eine integer Implementierung ähnlich kompakt (bezüglich Rechenschritten und Operanden).

#7387485
Lesenswert?

Rolf M. schrieb:

Abrunden, Aufrunden, etc. kann man hier nur mir Integern immer exakt machen.

Was verstehst du unter "exakt" im Zusammenhang mit Rundung? Runden ist etwas, das man tut, weil es nicht exakt geht oder zu aufwändig wäre.

Teilt man einen diskreten Wertebereich, in mehrere beliebige Halbintervalle, und schaut sich die Werte an den Enden an, passen diese exakt zusammen. Das heisst, nimmt man die Differenz (ende - start) aller teile, und zählt es zusammen, erhält man wieder den ursprünglichen Wertebereich. Bei anderen Intervallen ist es nicht so.

Es gibt da deshalb auch einige interessante Parallelen zwischen summen solcher diskreter Halbintervalle und Integralen, gewisse Klassen von Problemen sind dort auch äquivalent. Ich erinnere mich aber gerade nicht mehr, was da das Stichwort war. (FDE scheint damit zutun zu haben, aber ist nicht ganz was ich meine). Ich muss mal suchen, ob ich das wieder finde.

Jedenfalls ist es mit Integern sehr einfach, Wertebereiche so zu unterteilen, dass man quasi konsistent am Limit ist. Diese passen dann exakt zusammen, die gesamte Menge und die Abstände bleiben konsistent. Aber bei Floats kann ich das nicht wirklich machen.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren