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?
Rechne es doch einfach in float oder scaliere um den Faktor 10 höher, wenn es unbedingt in 8-Bit ausgeführt werden soll und teile das Ergebnis später wieder durch 10
Rechne es doch einfach in float oder scaliere um den Faktor 10 höher,
wenn es unbedingt in 8-Bit ausgeführt werden soll und teile das Ergebnis
später wieder durch 10
Faktor 10 wird bei einem Wert von 100 und 8 Bit schwierig. Aber ja, float bzw. double würde sich anbieten, gerade auf dem PC.
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.
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_tlinearFunction(uint8_tx){
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.
Wie rechnet man mit gebrochenen Steigungen ? Um sie zu einmalig berechnen kann man ja einmalig eine Floatingpoint Division verwenden. Nachher bei der Anwendung verwendet man einen 32bit Multiplikator und eine Division, welche nur shifts sind also zB div 65536, dh shift 16, dh niederwertiges word weg.
Nimm float, selbst auf 8-Bittern (AVR) geht das für viele Anwendungen ausreichend schnell. Den ganzen Ärger mit Rundungsfehlern und Überläufen muß ich nicht mehr haben.
Nimm float, selbst auf 8-Bittern (AVR) geht das für viele Anwendungen
ausreichend schnell. Den ganzen Ärger mit Rundungsfehlern und Überläufen
muß ich nicht mehr haben.
Genau das denke ich mir halt auch, warum sich festnageln auf uint8_t? Das sehe ich hier halt aktuell auch noch nicht.
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()
Aber genau mit float hat man dann doch Rundungsfehler?
Aber bedeutend weniger als mit einem Integer.
Der Vorteil ist, dass ich zu 100% immer genau weiß, wie groß dieser Rundungsfehler ist. ;-) Bei floats stochert man immer irgendetwas im Dunkeln oder ignoriert es einfach.
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.
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
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.
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).
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.
Rechne es doch einfach in float oder scaliere um den Faktor 10 höher,
wenn es unbedingt in 8-Bit ausgeführt werden soll und teile das Ergebnis
später wieder durch 10
Das dürfte wohl kaum reichen. Schau dir mal die benötigten Multiplikationen an und die Fehler, die bei geringer Aussteuerung entstehen.