tach,
ich habe mal ne frage zum Code.
Ich habe einen Wert [0..100] und möchte diesen auf [0..250] mappen. Das
ganze für einen AVR.
was wäre das bessere vorgehen?
1
staticuint8_tmapToPWM(uint8_tspeed)
2
{
3
uint8_tspeedPWM;
4
if(100>speed)
5
{
6
speed=100;
7
}
8
speedPWM=(uint8_t)((float)speed*2.5);
9
speedPWM=(uint8_t)(((uint16_t)speed*25U)/10U);
10
returnspeedPWM;
11
}
einmal caste ich speed auf float und berechne dann speedPWM.
beim zweiten mal umgehe ich float.
Welches wäre schneller/besser?
Wie kann man solche sachen rausfinden was schneller ist (auch gerne am
PC).
Lars schrieb:> Ich habe einen Wert [0..100] und möchte diesen auf [0..250] mappen.
Diese Werte sind echt ungeschickt: 101 Werte werden auf 251 Werte
abgebildet, das gibt einen echt üblen Teiler...
> Das ganze für einen AVR.
Ich würde hier sowas probieren:
1
speedPWM=(uint8_t)(((uint16_t)speed*640)/256);
Denn so eine unsigned Division durch 256 lässt sich vom Compiler super
optimieren: er ignoriert einfach das untere Byte.
Lothar M. schrieb:>> Ich habe einen Wert [0..100] und möchte diesen auf [0..250] mappen.> Diese Werte sind echt ungeschickt: 101 Werte werden auf 251 Werte> abgebildet, das gibt einen echt üblen Teiler...
0..100 sind prozentangaben, welche dann auf 0..255 gemapt werden sollen.
die letzten 5 habe ich mir gespart, damit ich entweder nur mit 2,5
multiplizerien muss (anstelle von 2,55) und bei der zweiten methode nur
mit 25 multiplizieren muss und mit 10 dividieren (anstelle von 255 und
100.
Lothar M. schrieb:> speedPWM = (uint8_t)(((uint16_t)speed * 640) / 256);> Denn so eine unsigned Division durch 256 lässt sich vom Compiler super> optimieren: er lässt einfach das untere Byte weg.
ah ok, das muss ich mir merken.
Georg G. schrieb:> Bist du sicher, dass es nicht (speed > 100) heissen sollte?
ja, da hast du recht
Lars schrieb:> Georg G. schrieb:>> Bist du sicher, dass es nicht (speed > 100) heissen sollte?>> ja, da hast du recht
Bzw. ich wollte
(100 <= speed) schreiben. Somit wäre die Konstante auf der linken seite.
(wurde mir gesagt das man das so macht, um eine Zuweisung zu vermeiden.
Lars schrieb:> die letzten 5 habe ich mir gespart
Warum?
Beim float kostet das überhaupt nicht mehr und bei deinem Ansatz musst
du doch nur eine Stelle dazunehmen:
speedPWM = (uint8_t)(((uint16_t)speed * 255U) / 100U);
Und bei meinem Ansatz kommt dann halt
speedPWM = (uint8_t)(((uint16_t)speed * 653) / 256);
heraus.
Lars schrieb:> Georg G. schrieb:>> Bist du sicher, dass es nicht (speed > 100) heissen sollte?> ja, da hast du recht
Ich würde darüber hinaus nicht den Eingangswert begrenzen, sondern den
Ausgabewert:
1
staticuint8_tmapToPWM(uint8_tspeed)
2
{
3
uint16_th;
4
if(speed>100)h=65535;
5
elseh=(uint16_t)speed*655;
6
7
returnh/256;
8
}
Lustigerweise sieht man hier, dass eigentlich nicht 653, sondern 655 der
richtige Faktor ist. Das kommt, weil eben nicht 100 "gerade mal so" 255
ergeben muss, sondern am besten schon 255,99 wobei dann die ,99 eben
einfach abgeschnitten werden.
Lars schrieb:> (wurde mir gesagt das man das so macht, um eine Zuweisung zu vermeiden.
Das passiert aber bei "kleiner" und "größer" nicht, sondern nur bei
"gleich":
1
if(speed=100)...
Aus diesem Grund kann man da so schreiben:
1
if(100==speed)...
Und dann kommen die "vereinfachenden" Generalisten und sagen
stumpfsinning:
"Die Konstante muss bei Vergleichen immer nach links!"
Lothar M. schrieb:> Lars schrieb:>> Ich habe einen Wert [0..100] und möchte diesen auf [0..250] mappen.> Diese Werte sind echt ungeschickt: 101 Werte werden auf 251 Werte> abgebildet, das gibt einen echt üblen Teiler...>>> Das ganze für einen AVR.
Beide Versionen sind auf einem 8 Bitter eher aufwendig.
Probiert es doch mal mit:
Wert:= Wert * 5; // macht 0..500
oder
Wert:= (Wert << 2) + Wert; // macht auch 0..500
// d.h. (0..400)+(0..100)
Und dann
Wert:= Wert >> 1; // macht 0..250
oder gar
Wert:= (Wert << 1) + (Wert >> 1);
// macht (0..200) + (0..50)
Das wär's dann. Und mit etwas Nachdenken paßt das gerade so in ein Byte,
da braucht man nicht mal 16 Bit Arithmetik.
W.S.
Lothar M. schrieb:> Lustigerweise sieht man hier, dass eigentlich nicht 653, sondern 655 der> richtige Faktor ist. Das kommt, weil eben nicht 100 "gerade mal so" 255> ergeben muss, sondern am besten schon 255,99 wobei dann die ,99 eben> einfach abgeschnitten werden.
Hast Du das ausgerechnet, ob das keinen truncation-error gibt (kenne
kein deutsches Wort)? Klassisch wäre ja h = ((uint16_t)speed + 128) *
654 / 256;, aber es gibt ja durchaus Kombination, wo der
Trunkierungsfehler (gibt es das Wort?) verschwindet.
Walter T. schrieb:> Hast Du das ausgerechnet, ob das keinen truncation-error gibt
Da das Ganze in Einerschritten vorgegeben wird und schlußendlich 2.5er
Schritte draus werden sollen, hat man dank der Rasterung auf
Einerschritte immer mit im Detail ungleichmäßig erscheinenden
Schrittweiten zu tun. Da hilft garnix, außer dem Übergang auf float.
Aber der ist hier ja nicht gefragt.
W.S.
Klaus W. schrieb:> Und falls du genug Speicher hast und dafür mehr Geschwindigkeit haben> willst,...
Man müßte hier mal einen Kenner der AVR-Assemblerebene fragen, ob der
Zugriff auf ein Array nicht eventuell einen Tick langsamer ist als 2
Verschiebungen und eine Addition - und beides in 8 Bit.
W.S.
Der Kenner heißt gcc.
Mit gewünschter Optimierung kompilieren, und Code anschauen.
Mein Prognose, was am schnellsten ist, will ich noch nicht verraten ...
Walter T. schrieb:> Hast Du das ausgerechnet, ob das keinen truncation-error gibt
Ja, da muss man aufpassen. Aber generell muss hier ja nichts gerundet
werden, weil sich auf jede Art immer irgendein Stolperer im Verlauf
ergibt.
W.S. schrieb:> schlußendlich 2.5er Schritte draus werden sollen
Eigentlich sollen da korrekterweise 2,48er Schritte draus werden, denn
wenn 101 mögliche Werte auf 251 mögliche Werte abgebildet werden sollen,
dann kommt da 251/101 = 2,48 heraus. Mit dieser 0 am Anfang, die ja auch
einen "Wert" hat (sonst bräuchte man sie nicht), tun sich Programmierer
augenscheinlich irgendwie schwer.
Und ganz eigentlich sollte der Wertebereich ja auf 0..255 aufgeweitet
werden und nur wegen der komplizierten Rechnung wurde auf die "letzten
5" verzichtet.
Lothar M. schrieb:> Eigentlich sollen da korrekterweise 2,48er Schritte draus werden,...
aber so ein Wert ist etwas weniger gut in Integer ausdrückbar. Also
schenken wir uns das.
W.S.
W.S. schrieb:> aber so ein Wert ist etwas weniger gut in Integer ausdrückbar. Also> schenken wir uns das.
Mir ist es im Prinzip schnurz, ob das Ding läuft oder nicht. Ich will
einfach darauf hinweisen, dass man sich doch besser vorher Gedanken zu
Wertebereichen machen sollte, damit sich der Rechner hinterher leichter
tut. Der ist nun mal voll super in binären Dingen und schon das
menschliche Zehnersystem macht ihm zu schaffen.
Oder man macht es wie der TO Lars, schneidet der Einfachheit halber
hinten halt auch noch 5 ab und nimmt das Ganze als gottgegeben hin.
Hauptsache "Meine Software läuft!" und ganz agil "Gut genug reicht aus!"
> Also schenken wir uns das.
Trotzdem macht sich Lars aber Gedanken ums Thema und ich zeige ihm den
Tellerrand. Ob er drüber hinausschauen will, das liegt an ihm.
Klaus W. schrieb:> willst, geht für sowas auch eine Tabelle:
+1.
der Variablenname "speed" lässt darauf schließen, dass es wohl um einen
Motor geht, der hinter der PWM hängt.
Dessen "Geschwindigkeit" hängt ganz sicher nicht linear vom PWM-Tastgrad
ab, vermutlich läuft er bei den niedrigsten Werten noch nicht mal los.
mit der Tabelle kannst du das so hinbiegen, dass deine Stellwerte 0…100%
auch ungefähr den prozentualen Motor-U/min entsprechen.
Lothar M. schrieb:> Das passiert aber bei "kleiner" und "größer" nicht, sondern nur bei> "gleich":
1
if(speed=100)...
> Aus diesem Grund kann man da so schreiben:
1
if(100==speed)...
Ergänzend möchte ich dem TO noch sagen, dass im ersten Fall immerhin
eine Warnung kommt, z.B. derart (so mein WinAVR2010):
warning: suggest parentheses around assignment used as truth value
denn der Compiler erkennt, dass diese Abfrage immer 'true'(bzw. 'false',
bei der Abfrage auf Null) liefern wird und hält es für wahrscheinlich,
dass man es so nicht meinte/wollte. Ein 'if' ist in dem Zusammenhang
meist wenig sinnvoll ...
Während bei der anderen Schreibweise, die fälschlicherweise dann so
aussieht:
1
if(100=speed)...
führt das zu einem Error, denn einer Zahl ist kein Wert zuweisbar und
damit ist der Code nicht kompilierbar.
Wenn man also einigermaßen diszipliniert ist und sich die Warnungen
anschaut und diese beseitigt, dann fällt das auch bei der 'normalen'
Schreibweise auf.
Klaus W. schrieb:> Der Kenner heißt gcc.> Mit gewünschter Optimierung kompilieren, und Code anschauen.>> Mein Prognose, was am schnellsten ist, will ich noch nicht verraten ...
So ist es! Optimiert wird ganz am Schluss - notfalls nimmt man einen
schnelleren Prozessor.
Was man auf einen Controller (8bit) aber auf jeden Fall vermeiden sollte
ist float oder gar double.
Aber auch eine Division ist recht teuer. Wenn es irgendwie geht, bildet
man diese auf eine Multiplikation ab.
Ich musste mal Minuten auf ein Byte aufdehnen, also mit 256/60
multiplizieren. Mein damaliger Kommentar hierzu:
// Skalierung *256/60 = 128/30 = 64/15 (= 4.2666...)
//
// Näherungsweise kann auch mit dem Wert 68/16 = 17/4
// (= 4.25 -> 0.392% Fehler) gerechnet werden.
// Die notwendige Divison lässt sich dann als >>2 sehr schnell
// ausführen.
Hab's dann aber mit einer Tabelleninterpolation realisiert (ich musste
da noch was entzerren...).
Tabelleninterpolationen realisiert man vorteilhafterweise mit 9, 17 oder
33 Stützstellen entsprechend 8, 16 oder 32 Intervallen. Denn dann
vereinfacht sich die notwendige Division auch auf einfaches schieben.
Aber das wissen heute schon die Compiler.
Ach ja, zum Thema "casten" wollt ich noch was sagen...
Casts in einem C-Programm sind der fast sichere Hinweis, dass irgend was
ganz mächtig aus dem Ruder läuft. Nicht umsonst schmeißt da der Compiler
eine Warning. Definiert man seine Datentypen richtig (und nicht
irgendwie) brauchts keinen Cast, oft ist der auch noch so was von falsch
(z.B. cast: int -> float).
Anbei noch der Beispiel-Code einer Tabelleninterpolation:
1
// I_table[] beschreibt die Sensor-Kennlinie.
2
3
intI_table[17]={
4
-355,-237,-119,-38,29,88,143,197,251,307,368,
5
436,515,615,756,1012,1268
6
};
7
8
/**
9
* \brief Konvertiert das ADC Ergebnis in einen Temperaturwert.
10
*
11
* \param adc_value Das gewandelte ADC Ergebnis
12
* \return Die Temperatur in 0.1 °C
13
*/
14
15
intI_ADC2Temp(unsignedcharadc_value){
16
17
intp1,p2;
18
19
/* Stützpunkt vor und nach dem ADC Wert ermitteln. */
20
p1=I_table[(adc_value/16)];
21
p2=I_table[(adc_value/16)+1];
22
23
/* Zwischen beiden Punkten linear interpolieren. */
Uwe B. schrieb:> Was spricht gegen> return (2 * speed + speed / 2)
Ist ja sowas von aaalt, kuck da:
W.S. schrieb:> oder gar Wert:= (Wert << 1) + (Wert >> 1);
Lars schrieb:> Welches wäre schneller/besser?
Was der Compiler daraus macht. Der ist nicht halb so dumm wie Du (und
ich, nicht daß das jetzt der Punkt wäre).
Deine Frage betrifft die klassischen Prinzipien von "premature
optimization is the root of all evil" (Donald E. Knuth) und "measure,
don't guess" (Kirk Pepperdine).
Mein Rat an Dich ist mit dem Unfug aufzuhören. Beherzige die klassischen
Tipps: "make it work, make it right, make it fast" (Kent Beck). Diese
Reihenfolge.
Uwe B. schrieb:> Was spricht gegen> return (2 * speed + speed / 2)
Dagegen spricht eigentlich nur, dass die Skalierung eigentlich von
0..100 auf 0..255 geschehen sollte und Lars zur "Vereinfachung" gleich
mal 5 Inkremente weggelassen hat, denn
Lars schrieb:>>> 0..100 sind prozentangaben, welche dann auf 0..255 gemapt werden sollen.>>> die letzten 5 habe ich mir gespart, damit ich entweder nur mit 2,5>>> multiplizerien muss
Und wenn man mit Murks anfängt, dann kommt auch nach einer optimalen
Optimierung nur optimierter Murks heraus.
Was ich hier eigentlich laufend sagen will:
das Problem ist hier, dass Lars zwischendurch zum vereinfachten Rechnen
seine allzu menschlichen 0..100 Prozent sehen will. Das macht die Arbeit
für den µC dann eben umständlich.
Sinnvollerweise sollte er schon vorher nicht mit den auf dem
Zehnfingersystem basierenden 0..100% herumrechnen, sondern gleich mit
den binären 0..255, die dem µC so richtig reinlaufen.
jo schrieb:> Anbei noch der Beispiel-Code einer Tabelleninterpolation:
Sowas ist super geeignet zum Umbiegen von nichtlinearen Frunktionen.
Allerdings heben wir hier eine ganz einfache völlig lineare Funktion. In
deinem Sinn also eigentlich eine Tabelle, die nur 2 Einträge 0 und 255
hat, zwischen denen interpoliert wird. Aber eben ungeschickterweise mit
einer Zehnerpotenz (statt wie bei dir mit einer Zweierpotenz):
1
/* Tabellenstützpunkte */
2
p1=0;
3
p2=255;
4
5
/* Zwischen beiden Punkten linear interpolieren. */
Lothar M. schrieb:> Was ich hier eigentlich laufend sagen will:> das Problem ist hier, dass Lars zwischendurch zum vereinfachten Rechnen> seine allzu menschlichen 0..100 Prozent sehen will
Jetzt bin ich allerdings neugierig: Bei mir sind die Stellen, bei denen
solche Zahlen vorkommen, GUI und Konfigurationsdaten (.ini-Datei oder
Einstellungsmenü). Meist in .5%- oder .1%-Schritten verstellbar.
Wie machst Du das? Mutest Du dem Benutzer Binärprozente zu, oder
schluckst Du die Kröte, das Menschen Dezimalwerte intuitiver handhaben
können?
Walter T. schrieb:> Wie machst Du das? Mutest Du dem Benutzer Binärprozente zu, oder> schluckst Du die Kröte, das Menschen Dezimalwerte intuitiver handhaben> können?
Am UserInterface darf der Bediener gerne seine geliebte
Zehnfinger-Arithmetik sehen. Aber das wird im Programm gleich ins
passende maschinenfreundliche Binärformat gewandelt. Und da sind dann
100% eben 255 oder 65535 oder sonst welche 2^n-1 Werte.
> Mutest Du dem Benutzer Binärprozente zu
Bei Sachen, die sowieso nicht intuitiv erfassbar oder messbar sind (wie
z.B. Zeitkonstanten, die man nicht mit der Stoppuhr messen kann oder
Schaltschwellen, die man nicht mit dem Multimeter oder einem Maßband
messen kann) da geht der Wertebereich dann durchaus auch mal von
dimensionslosen 0..255 oder 0..1023.
Wenn aber an dieser obigen "speed" z.B. nur ein Poti als Eingabeelement
am ADC hängt, dann ist es unsinnig, den 10-Bit ADC-Wert in 0..100%
umzurechnen und dann die 0..100% wiederum in einem 8-Bit PWM-Wert.
Da hätte man mit ein wenig Nachdenken leichter die unteren 2 Bit
abgeschnitten und den ADC-Wert direkt an die PWM gegeben.
Lothar M. schrieb:> jo schrieb:>> Anbei noch der Beispiel-Code einer Tabelleninterpolation:> Sowas ist super geeignet zum Umbiegen von nichtlinearen Frunktionen.> Allerdings heben wir hier eine ganz einfache völlig lineare Funktion. In> deinem Sinn also eigentlich eine Tabelle, die nur 2 Einträge 0 und 255> hat, zwischen denen interpoliert wird. Aber eben ungeschickterweise mit> einer Zehnerpotenz (statt wie bei dir mit einer Zweierpotenz):> /* Tabellenstützpunkte */> p1 = 0;> p2 = 255;> /* Zwischen beiden Punkten linear interpolieren. */> return p1 + ((p2-p1) * speed) / 100;
Das hatte ich gezeigt, auch das WIESO, weil ich noch was entzerren
musste.
jo schrieb:> Hab's dann aber mit einer Tabelleninterpolation realisiert (ich musste> da noch was entzerren...).
Im meinem Beitag davor (eben #6862088) hatte ich gezeigt, wie man ganz
allgemein Näherungslösungen entwickeln kann - z.B. indem man statt mit
255/100 mit 163/64 (~ 0,3% Fehler) rechnet - genau so, wie von Dir in
der allerersten Antwort schon beispielhaft gezeigt.
Was ich ganz sicher NICHT tun werde, ist schlüsselfertige Lösungen für
Lars' "Hobbykram" zu entwickeln.
just my 2 ct
Uwe B. schrieb:> Was spricht gegen> return (2 * speed + speed / 2)
Mathematisch nichts, da könnte man auch Wert:= Wert * 2.55; schreiben.
Liest sich auch recht kurz.
Aber:
Das Ganze soll ein Aufspreizen von 0..100 auf 0..250 (eigentlich 0.255)
sein, was bei einem Integer Ergebnis eben immer zu Rundungen führt.
Eigentlich sehe ich beim Anliegen des TO nur das Übersetzen von einer
Prozentzahl in
"aus-wenig-viel-ganz viel"
Mehr nicht.
Und was bei dir dagegen spricht, ist die Division. Die kostet auf einem
8 Bit Controller viel mehr, als sie im vorliegenden Falle nützt.
W.S.
W.S. schrieb:> Und was bei dir dagegen spricht, ist die Division. Die kostet auf einem> 8 Bit Controller viel mehr, als sie im vorliegenden Falle nützt.
Lassen wir die Kirche im Dorf. Division durch zwei ist billig. Selbst
wenn 2 aus irgendeinem Grund volatile sein sollte.
W.S. schrieb:> Und was bei dir dagegen spricht, ist die Division. Die kostet auf einem> 8 Bit Controller viel mehr, als sie im vorliegenden Falle nützt.jo schrieb:> vereinfacht sich die notwendige Division auch auf einfaches schieben.> Aber das wissen heute schon die Compiler.
Frage:
Und wenn man dem Compiler das nicht zu traut, schreibt man dann statt
Uwe's "speed / 2" nicht besser gleich "speed >> 1" - so wie von W.S.
vorgeschlagen?
W.S. schrieb:> Und dann Wert:= Wert >> 1;
Lothar M. schrieb:> W.S. schrieb:>> schlußendlich 2.5er Schritte draus werden sollen> Eigentlich sollen da korrekterweise 2,48er Schritte draus werden, denn> wenn 101 mögliche Werte auf 251 mögliche Werte abgebildet werden sollen,> dann kommt da 251/101 = 2,48 heraus.
Aus 100 soll 250 werden. 100 * 2,48 ist aber nur 248. Das Ergebnis wäre
also falsch. Der Fehler liegt hier darin, dass du auf der einen Seite
die Anzahl der Werte nimmst, daraus dann aber die Größe der Schritte
ableitest. Bei 101 Werten gibt es aber nur 100 Schritte. Daher muss hier
jeder Schritt um Faktor 2,5 vergrößert werden.
> Mit dieser 0 am Anfang, die ja auch einen "Wert" hat (sonst bräuchte man> sie nicht), tun sich Programmierer augenscheinlich irgendwie schwer.
QED ;-)
Rolf M. schrieb:> 100 * 2,48 ist aber nur 248. Das Ergebnis wäre> also falsch.
Ähem, nein. Nicht wirklich. Bedenke mal, daß wir hier lediglich ein
nicht sonderlich feines Einer-Raster von 0 bi 100 auf ein Einer-Raster 0
bis 250 (oder 0 bis 255) abbilden wollen.
Daß dabei die jeweilige Schrittweite bzw. Grobheit so etwa 2.5 beträgt,
bedeutet eben auch, daß mit eben dieser Grobheit 248 recht gut für 250
durchgeht, es ist ja nur eine tatsächliche Abweichung von 2.0 und die
liegt unter der Grobheit von 2.5 bzw. 2.48.
Also lassen wir hier die Haarspalterei, denn im Grunde ist das Ganze nur
eine Einstellsache nach dem Motto "ein bissel mehr als dreiviertel" und
keine mathematisch exakte Angelegenheit.
W.S.
W.S. schrieb:> Rolf M. schrieb:>> 100 * 2,48 ist aber nur 248. Das Ergebnis wäre>> also falsch.>> Ähem, nein. Nicht wirklich. Bedenke mal, daß wir hier lediglich ein> nicht sonderlich feines Einer-Raster von 0 bi 100 auf ein Einer-Raster 0> bis 250 (oder 0 bis 255) abbilden wollen.
Es entspricht jedenfalls nicht dem initialen Wunsch, auf 0-250
abzubilden.
> Daß dabei die jeweilige Schrittweite bzw. Grobheit so etwa 2.5 beträgt,> bedeutet eben auch, daß mit eben dieser Grobheit 248 recht gut für 250> durchgeht, es ist ja nur eine tatsächliche Abweichung von 2.0 und die> liegt unter der Grobheit von 2.5 bzw. 2.48.
Aber wozu auf 2,48 verfälschen?
Frage: Wenn der Bereich statt auf 0-250 auf 0-300 gemappt werden müsste,
würdest du da 3 oder 2,98 als Faktor nehmen und warum? (Dass sich Faktor
3 leichter berechnen lässt, da Integer-tauglich mal außen vor gelassen)
> Also lassen wir hier die Haarspalterei, denn im Grunde ist das Ganze nur> eine Einstellsache nach dem Motto "ein bissel mehr als dreiviertel" und> keine mathematisch exakte Angelegenheit.
Solange klar ist, dass dann nicht auf die angedachten 0-250, sondern auf
0-248 gemappt wird und dass das keinerlei Vorteil bietet.
Auch wenn es für diesen Anwendungsfall wohl nicht so wichtig ist, halte
ich es für sinnvoll, darauf hinzuweisen, dass der Faktor 2,48 für das
gewünschte Mapping mathematisch nicht korrekt ist. Das wäre er auch dann
nicht, wenn der Ergebnistyp alle Werte exakt darstellen könnte.
Rolf M. schrieb:> Aber wozu auf 2,48 verfälschen?> Frage:
Also erstens: deine Frage gehört hier nicht hin, denn schließlich hat
der TO eine Aufspreizung der Prozentzahlen von 0 bis 100 auf den Bereich
eines Bytes vor und er hat zum Zwecke der Vereinfachung sich auf 0..250
zurückgenommen.
Und zweitens: dieses 2.48 kommt von Lothar. Der hatte nämlich im Sinn,
die Aufspreizung auf 255 anzuzielen.
Und eigentlich ist das Thema bereits komplett erledigt und was hier noch
kommen kann, ist Haarspalterei, denn die erzielbare Schrittweite bzw.
Grobheit ist hier in jedem Falle so etwa 2.5, was ja als solches nicht
integer darstellbar ist.
W.S.
W.S. schrieb:> Rolf M. schrieb:>> Aber wozu auf 2,48 verfälschen?>> Frage:>> Also erstens: deine Frage gehört hier nicht hin, denn schließlich hat> der TO eine Aufspreizung der Prozentzahlen von 0 bis 100 auf den Bereich> eines Bytes vor und er hat zum Zwecke der Vereinfachung sich auf 0..250> zurückgenommen.
Lothar hat behauptet, der Faktor müsse 2,48 betragen. Ich hatte
lediglich angemerkt, dass seine Rechnung, mit der er darauf kommt, nicht
stimmt. Dem hast du widersprochen. Und das ist erst mal ganz unabhängig
davon, ob der TO nun auf 250 oder 255 skalieren will und ob er das nun
"so ungefähr" oder genau will.
Rolf M. schrieb:> Lothar hat behauptet, der Faktor müsse 2,48 betragen.
Stimmt, aber es ist auch nicht ganz so einfach...
> Ich hatte lediglich angemerkt, dass seine Rechnung, mit der er darauf> kommt, nicht stimmt.
Der Witz ist hier, dass ja tatsächlich diskrete 101 Werte (0..100 sind
101 Werte) auf 251 Werte (0..250 sind 251 Werte) abgebildet werden
sollen. Insofern stimmt meine Rechnung mit 101/251 im Grunde schon.
Allerdings habe ich nicht fertig gedacht.
Wenn wir also mal den Eingangsbereich als feiner abgestuft mit 0,0 ..
100,0 anschauen würden, dann hätten wir statt der 101 Werte auf einmal
1001 Werte:
0,0 .. 0,1 .. 0,2 .... 99,8 .. 99,9 .. 100,0
Und auf der Ausgangsseite wären das dann 2501 mögliche Werte:
0,0 .. 0,1 .... 249,8 .. 249,9 .. 250,0
Das ergibt dann einen Faktor von 2501/1001 = 2,4985.
Oder wenn wir gar 2 Nachkommastellen mit anschauen, dann bekommen wir
10001 Werte eingangseitig und 25001 Werte ausgangsseitig: 2,49985.
Eine Grenzwertbetrachtung liegt nahe und bringt den bekannten Faktor
2,49999...99 oder eben gleich 2,5.
Aber wenn wir jetzt mal ansehen, was bei dieser Rechnung mit 2
Nachkommastellen herauskommen kann, dann wird es interessant. Sehen wir
mal als Resultat die 0 an: 0,01 ergibt bei der Umwandlung nach integer 0
genauso wie 0,22 oder 0,76 oder 0,9999, weil bei der Umwandlung von
float nach integer die Nachkommastellen einfach abgeschnitten werden.
Es gibt aber dank der "Obergrenze" von 250,00 eben kein 255,01 und auch
kein 255,22 und auch kein 255,99, das "zurechtgestutzt" werden müsste.
Der Witz ist also die Asymmetrie dieser 101 bzw 251 Werte, denn der
jeweils "höchste" Wert ist singulär. Es gibt nur 1 einzige Art, ihn zu
erreichen: nur von genau 100,00000 kommt man auf genau 250,00000
Und Obacht: jetzt verlasse ich die 0..250-Welt.
Ich bin also selbst über den OffByOne-Stein gestolpert, denn eigentlich
will der TO die 0..100 auf 0..256(kein Tippfehler!!) skalieren. Und
falls da dann wirklich 256 herauskommt, dann muss man dieses eine
Ergebnis auf hardwaretaugliche 255 zurechtstutzen.
Man tut sich da jetzt auf den ersten Blick etwas schwer, aber es wird
einfacher, wenn man mal 0...1000 auf 0..255 skalieren will:
1
E A
2
0 -> 0
3
1 -> 0
4
2 -> 0
5
3 -> 0
6
4 -> 1
7
5 -> 1
8
6 -> 1
9
:
10
:
So weit so klar...
Und jetzt am anderen Ende: was soll mit dem Wert 999 passieren?
Ergibt das dann noch 254 oder schon 255?
Kann nur der Wert 1000 den Wert 255 ergeben, so wie hier:
1
:
2
993 -> 253
3
994 -> 253
4
995 -> 253
5
996 -> 254
6
997 -> 254
7
998 -> 254
8
999 -> 254
9
1000 -> 255 (nur auf 1 Art erreichbar??)
Oder sollte es nicht eher so aussehen:
1
:
2
993 -> 254
3
994 -> 254
4
995 -> 254
5
996 -> 255
6
997 -> 255
7
998 -> 255
8
999 -> 255
9
1000 -> 255 (eigentlich 256, aber Begrenzung auf hardwaretaugliche 255!!)
Rolf M. schrieb:> Aus 100 soll 250 werden.
Genau das "wünscht" sich der To, es ist aber entweder a) fraglich, oder
eben b) eigentlich falsch.
Denn wenn mit der richtigen Steigung gerechnet werden müsste, wäre dann
im TO-Fall für 0...100 -> 0...250 nicht sogar der Faktor 2,50999
richtiger (oder eben 2,51 mit anschließender Begrenzung auf den
Maximalwert 250)?
W.S. schrieb:> Ich mach da nen Vorschlag zur Güte:
Der passt gut, wenns schnell gehen muss.
Aber ich würde auch gleich noch die Spreizung auf "fast" 255 reinpacken:
Erg = Wert + Wert + Wert/2 + Wert/16;
Oder eben den entsprechenden (Wert>>4).
Das gibt dann für 99 das Ergebnis 253, und der Grenzwert 100 wird ja
explizit mit 255 abgevespert.
SCNR ;-)