Hallo zusammen,
ich habe eine Knobelaufgabe, die mich noch wach hält. Ich will wissen,
ob sich die Variable c im Intervall zwischen a und a+b (inklusive)
befindet. Alle drei Variablen können alle Vorzeichen haben. Der Betrag
von b ist üblicherweise sehr viel kleiner als INT_MAX. Genutzt wird es
mit dem ARM-GCC auf einem Cortex-M3.
Die Lösung, die mir einfiel, war:
Walter T. schrieb:> Hallo zusammen,>> ich habe eine Knobelaufgabe, die mich noch wach hält. Ich will wissen,> ob sich die Variable c im Intervall zwischen a und a+b (inklusive)> befindet. Alle drei Variablen können alle Vorzeichen haben. Der Betrag> von b ist üblicherweise sehr viel kleiner als INT_MAX. Genutzt wird es> mit dem ARM-GCC auf einem Cortex-M3.>> Die Lösung, die mir einfiel, war:>
1
>/** Teste, ob c im Intervall [a; a+b] liegt */
2
>boolisdrin(inta,intb,intc)
3
>{
4
>return((a-c)*(a+b-c))<=0;
5
>}
6
>
>> Gibt es eine schönere Lösung?
Also eine schönere Lösung gibt es in jedem Fall. Namlich einfach die
Sache so hinzuschreiben, wie sie gedacht ist. Was du da hingeschrieben
hast, ist die klassische Klartextverschlüsselung, zu der diese
unsäglichen C-Fetischisten immer tendieren.
Ein andere Frage wäre, was die schnellste Lösung ist. Aber genau das
scheint auch gerade diese C-Fetischisten wiederum nur sehr peripher zu
tangieren. Wichtiger scheint zu sein, dass möglichst nicht mehr
erkennbar ist, was der Code eigentlich tut, die Sache aber formal
legaler C-Code ist.
Walter T. schrieb:> Gibt es eine schönere Lösung?
Ja, eine die der LESER sofort versteht, und nicht der Autor!
Code wird für MENSCHEN geschrieben, nicht für Compiler.
Was ist daran so schwer zu verstehen?
Moin,
Vielleicht kann man die Multiplikation im Beispiel noch durch eine
Addition von Logarithmen ersetzen. Kommt sicher gut in Lesbarkeit und
Performance.
SCNR
WK
Walter T. schrieb:> ich habe eine Knobelaufgabe, die mich noch wach hält.> Ich will wissen, ob sich die Variable c im Intervall> zwischen a und a+b (inklusive) befindet. Alle drei> Variablen können alle Vorzeichen haben. Der Betrag> von b ist üblicherweise sehr viel kleiner als INT_MAX.> Genutzt wird es mit dem ARM-GCC auf einem Cortex-M3.>> Die Lösung, die mir einfiel, war:> /** Teste, ob c im Intervall [a; a+b] liegt */> bool isdrin(int a, int b, int c)> {> return ((a-c)*(a+b-c)) <= 0;> }
Wenn ich das recht verstehe, dann verwendest Du, dass
eine quadratische Funktion, die eine nach oben geöffnete
Parabel beschreibt, außerhalb der Nullstellen einen
positiven und zwischen den Nullstellen einen negativen
Funktionswert liefert.
Die Idee kam mir auch schon mal :)
> Gibt es eine schönere Lösung?
Sagen wir so: Kompakter geht es kaum.
Man könnte unter Umstände auf Kosten der Kompaktheit besser
verständliche Lösungen finden, beispielsweise die, dass der
Betrag der Differenz von c zur Intervallmitte kleiner als
abs(b/2) sein muss, wenn c im Intervall liegen soll.
Dergute W. schrieb:> Vielleicht kann man die Multiplikation im Beispiel> noch durch eine Addition von Logarithmen ersetzen.> Kommt sicher gut in Lesbarkeit und Performance.
Der Spott ist verständlich, aber nur zum Teil
berechtigt. Wollte man Walters Problem mit Vergleichen
lösen, braucht man davon insgesamt fünf Stück. Je nach
Anwendungsfall ist das nicht gut für die Laufzeit.
Egon D. schrieb:> Der Spott ist verständlich, aber nur zum Teil> berechtigt. Wollte man Walters Problem mit Vergleichen> lösen, braucht man davon insgesamt fünf Stück.
Von denen müssten aber maximal 3 ausgeführt werden.
Walter T. schrieb:> Gibt es eine schönere Lösung?
Vor allem gibt es korrekte Lösungen. Die von dir vorgeschlagene
funktioniert nicht wegen Überlauf, Beispiel a = INT_MIN, c = INT_MAX.
Und von der Multiplikation wollen wir erst garnicht reden.
Eine korrektte Lösung:
1
// c in [a, a + b] <=> c - a in [0, b]
2
3
boolin_range(inta,intb,intc)
4
{
5
if(c>=a&&b>=0)
6
return(unsigned)c-(unsigned)a<=(unsigned)b;
7
else
8
returnfalse;
9
}
Falls c >= a ist c - a >= 0 und maximal INT_MAX - INT_MIN, was nicht
größer als UINT_MAX ist, so dass unsigned genügt um einen Überlauf
auszuschließen. C-Standard definiert (unsigned) c effektiv als c + 2^N
für c < 0 bei N-Bit int. Daher funktionert der Vergleich mit unsigned.
b < 0 stellt die Leere Menge dar. Falls b < 0 das Intervall [a+b, a]
bedeuten soll, sieht es anders aus; dito wenn es das Komplement von [a,
a+b] bedeuten soll.
Egon D. schrieb:> Wollte man Walters Problem mit Vergleichen> lösen, braucht man davon insgesamt fünf Stück.
Innerhalb des Gültigkeitsbereichs der gewählten Datentypen? Da würde
jeder mit einer Addition und zwei Vergleichen auskommen...
c-hater schrieb:> Egon D. schrieb:>>> Wollte man Walters Problem mit Vergleichen>> lösen, braucht man davon insgesamt fünf Stück.>> Innerhalb des Gültigkeitsbereichs der gewählten> Datentypen? Da würde jeder mit einer Addition und> zwei Vergleichen auskommen...
Lass' sehen.
Und daran denken: Wir reden von Ganzzahlen MIT
Vorzeichen.
Johann L. schrieb:> b < 0 stellt die Leere Menge dar.
???
"b < 0" bedeutet einfach nur, dass "a+b" kleiner als
"a" ist.
> Falls b < 0 das Intervall [a+b, a] bedeuten soll,> sieht es anders aus;
Ja... das war ja gerade der Witz.
Johann L. schrieb:> Eine korrektte Lösung:
[...]
Nein, korrekt ist die auch nicht (höchstens rein formal).
Sie basiert auch wieder nur darauf, den Bereich der vorgegebenen Typen
zu verlassen. Deutlich erkennbar an den übersprudelnden Typecasts.
c-hater schrieb:> Johann L. schrieb:>>> Eine korrektte Lösung:>> [...]>> Nein, korrekt ist die auch nicht (höchstens rein formal).
Was daran ist falsch?
Johann L. schrieb:> Was daran ist falsch?
Formal nichts. Die Lösung zeigt nur überdeutlich, dass das Typsystem
eigentlich unnütz (weil leicht zu umgehen) und oft hinderlich ist (weil
man es umgehen muss, um zu einer effizienten Lösung zu kommen).
Asm rules!
c-hater schrieb:> Innerhalb des Gültigkeitsbereichs der gewählten Datentypen? Da würde> jeder mit einer Addition und zwei Vergleichen auskommen...
das denke ich auch.
Voraussetzung: das Intervall wird richtig übergeben.
[a,a+b]
wird bei negativen Zahlen falsch dargestellt.
z.B. a=-1, b=-2 --> [-1, -3] und diese Reihenfolge ist natürlich falsch.
[-3, -1] wäre richtig. Wenn das Intervall als Parameter richtig
übergeben wird, dann reichen zwei Vergleich und eine Addition.
zitter_ned_aso schrieb:> Voraussetzung: das Intervall wird richtig übergeben.
Der TO hat in seinem Ursprungsbeitrag AUSDRÜCKLICH
darauf hingewiesen, dass diese Bedingung NICHT erfüllt
ist.
Was soll das also?
Johann L. schrieb:> Vor allem gibt es korrekte Lösungen. Die von dir vorgeschlagene> funktioniert nicht wegen Überlauf
Du hast Recht. Das ist mir beim Herunterfahren des Rechners auch noch
eingefallen, aber da ich dachte, das das Prinzip klar wäre, habe ich die
Korrektur auf heute morgen verschoben. Richtig wäre für meine Anwendung
(da a und c nie besonders weit auseinanderliegen):
1
/** Teste, ob c im Intervall [a; a+b] liegt */
2
boolisdrin(inta,intb,intc)
3
{
4
return((a-c)*(a-c+b))<=0;
5
}
Egon D. schrieb:> enn ich das recht verstehe, dann verwendest Du, dass> eine quadratische Funktion, die eine nach oben geöffnete> Parabel beschreibt, außerhalb der Nullstellen einen> positiven und zwischen den Nullstellen einen negativen> Funktionswert liefert.
Ich bin einfacher gestrickt: Haben zwei Zahlen unterschiedliche
Vorzeichen, ist ihr Produkt negativ.
zitter_ned_aso schrieb:> Das Intervall wird richtig übergeben.
Da wird die Hälfte der Aufgabe einfach weggelassen. Die Intervallgrenzen
zu sortieren kostet eventuell mehr Zeit als meine häßliche Variante mit
der Multiplikation.
c-hater schrieb:> Also eine schönere Lösung gibt es in jedem Fall. Namlich einfach die> Sache so hinzuschreiben, wie sie gedacht ist.
Zeig mal! Gerne in der Nicht-C-Sprache Deiner Wahl.
Die schönste Lösung wäre die Standard-Implementation mit 2 if.
Die versteht ein Mensch sofort, und muss nicht denken 'macht macht der
Code hier überhaupt'.
Und auch für den Prozessor wird es am einfachsten sein, wenn er nicht
multiplizieren muss. Das geht wohl am schnellsten und spart deshalb CO2.
Eine Lösung ist nicht unbedingt schön, nur weil sie mathematische
Spitzfindigkeien nutzt, oder in eine Zeile passt.
Johann L. schrieb:> ...tatsächlich ist es genau der Code, den GCC für folgende Vergleiche> erzeugt:
Ganz genau.
Schreib es so hin, wie es am besten lesbar ist und überlasse das
Optimieren gefälligst dem Compiler.
Egon D. schrieb:> Der TO hat in seinem Ursprungsbeitrag AUSDRÜCKLICH> darauf hingewiesen, dass diese Bedingung NICHT erfüllt> ist.>> Was soll das also?Walter T. schrieb:> ob sich die Variable c im Intervall zwischen a und a+b (inklusive)> befindet
x aus [a, a+b] genau dann wenn a<=x und x<=(a+b). Das ist ein Intervall.
Die Zahlen sind aufsteigen sortiert.
zitter_ned_aso schrieb:> [a,a+b]>> wird bei negativen Zahlen falsch dargestellt.>> z.B. a=-1, b=-2 --> [-1, -3] und diese Reihenfolge ist natürlich falsch.
[-1, -3] ist kein Intervall, sondern eine leere Menge.
Walter T. schrieb:> /** Teste, ob c im Intervall [a; a+b] liegt */> bool isdrin(int a, int b, int c)> {> return ((a-c)*(a-c+b)) <= 0;> }
Leichter lesbar ist das aber auch nicht.
Ich würde die Lösung über Minima bzw. Maxima bevorzugen, da selbige für
den Menschen besser lesbar ist. Das man dafür bei C erst 2 Macros
definieren muß
ist zwar weniger schön, tut der Übersicht aber keinen Abbruch.
Walter T. schrieb:> c-hater schrieb:>> Also eine schönere Lösung gibt es in jedem Fall. Namlich einfach die>> Sache so hinzuschreiben, wie sie gedacht ist.>> Zeig mal! Gerne in der Nicht-C-Sprache Deiner Wahl.
In den nicht C-Sprachen meiner Wahl würde ich das genau so lösen, denn
die haben Minimum- und Maximumfunktion implementiert. Da ist es sogar
völlig wurscht ob es Ganzzahlen oder Fließkommazahlen sind.
c-heater schrieb:> return (c >= a) == (c <= (a+b));
Dann rechne das mal für c=-2, a=-2 und b=-1 aus.
(-2 >= -2) != (-2 <= ((-2) + (-1)))
wahr nicht wahr
c=-2 würde aber im Intervall -2, -3 liegen.
Mit Minima und Maxima funktioniert es definitiv.
zitter_ned_aso schrieb:> Die Zahlen sind aufsteigen sortiert.
Da hast du wohl diesen Teil überlesen:
Walter T. schrieb:> Alle drei Variablen können alle Vorzeichen haben.
Das heißt, b kann negativ sein, und dann ist a+b kleiner als a.
zitter_ned_aso schrieb:> [-1, -3] ist kein Intervall, sondern eine leere Menge.
Das war hier ganz offensichtlich nicht gemeint.
Egon D. schrieb:> zitter_ned_aso schrieb:>>> Voraussetzung: das Intervall wird richtig übergeben.>> Der TO hat in seinem Ursprungsbeitrag AUSDRÜCKLICH> darauf hingewiesen, dass diese Bedingung NICHT erfüllt> ist.
Das sehe ich nicht so. Das
Walter T. schrieb:> Intervall zwischen a und a+b (inklusive)
ist das Intervall [a, a+b]. Wenn ich Walter richtig verstanden habe,
will er aber nicht auf
c ∈ [a, a+b],
sondern auf
c ∈ [a, a+b] ∪ [a+b, a]
prüfen.
c-heater schrieb:> return (c >= a) == (c <= (a+b));
Fast richtig. Für b < 0 und c ∈ {a, a+b} stimmt das Ergebnis aber nicht.
1
return((c>a)==(c<a+b))||(c==a)||(c==a+b);
sollte funktionieren unter der Voraussetzung, dass a+b keinen Überlauf
verursacht. Der Ausdsruck wird in 16 bis 24 Zyklen berechnet.
Ersetzt man || durch |, ist die Laufzeit konstant 16 Zyklen:
Zeno schrieb:> P. S. schrieb:>> Warum nicht einfach so etwas in dieser Art?>> Noch mehr Zeilen gehen nicht?
Für jemanden der Code-Qualität anhand der LOC beurteilt sicher nicht die
beste Lösung, das ist korrekt.
P. S. schrieb:> // if c out of intervall> if (c > upper_bound || c < lower_bound)> {> ret = false;> }> else> {> ret = true;> }>> return ret;
Das ist Quark hochsiebzehn! Einfach nur unfassbarer Blödsinn!
Erfahrener Entwickler schrieb:> Das ist Quark hochsiebzehn! Einfach nur unfassbarer Blödsinn!
Warum? Nur weil es mehr Zeilen (im Quelltext) beansprucht?
Ich hätte jetzt gerne siebzehn Gründe von Dir.
Erfahrener Entwickler schrieb:> Yalu X. schrieb:>> Das sehe ich nicht so.>> Das ist halt falsch.>> Einfach lesen:>> Walter T. schrieb:>> Alle drei Variablen können alle Vorzeichen haben.
Wir warten weiterhin auf deine Begründung, warum das falsch ist, auf
alle 17 Gründe.
Hier noch eine Lösung, die
1) Für alle Werte das richtige Ergebnis liefert.
2) In der Bitbreite des Eingabetyps rechnet, also 16-Bit Arithmetik bei
int16_t ermöglicht (abhängig von Instruktionssatz). Es wird also nicht
der nächst breitere Typ benötigt um einen Overflow zu vermeiden. Daher
kann problemlos auf int64_t portiert werden.
3) Nur Vergleiche (6), Subtraktion (1) und Negation (2) braucht, also
auch für Controller ohne MUL-Instruktion geeignet ist.
Hier beispielhaft für 16-Bit Integer:
1
// c in [a, a+b] u [a+b, a].
2
3
boolin_range(int16_tc,int16_ta,int16_tb)
4
{
5
uint16_tc_a=(uint16_t)c-(uint16_t)a;
6
7
if(b>=0&&c>=a)
8
returnc_a<=(uint16_t)b;
9
10
if(b<=0&&c<=a)
11
return-c_a<=-(uint16_t)b;
12
13
returnfalse;
14
}
Für int dann analog mit unsigned statt uint16_t.
Nachteile:
A) Schwieriger nachzuvollziehen als andere Lösungen, braucht daher
zusätzliche Kommentierung / Erklärung.
B) Setzt Darstellung negativer Zahlen im 2-er Komplement voraus, was
aber für gängige Architekturen der Fall ist. Portierbarkeit ist also
nur bedingt gegeben.
P. S. schrieb:> Für jemanden der Code-Qualität anhand der LOC beurteilt sicher nicht die> beste Lösung, das ist korrekt.
Ich beurteile Code nicht nach Anzahl der Zeilen. Wenn es lesbar ist
dürfen es gern auch ein paar Zeilen mehr sein.
Aber diese Lösung
Beitrag "Re: C: Prüfen, ob Variable in Intervall" ist
für das angefragte Problem einfach zu viel.
Gehen wir doch einfach mal mathematisch an das Problem heran und
überlegen wir uns mal zuerst, wie sich ein Bereich von Zahlen definiert.
Da kommen wir sehr schnell zu dem Ergebnis, das ein Zahlenbereich durch
einen Kleinstwert (Minimum) und einen Größtwert (Maximum) bestimmt ist.
Wenn ich die beiden Werte habe muß ich also nur noch prüfen ob mein Wert
zwischen diesen beiden Grenzen liegt und das geht übersichtlich in einer
Zeile. Mathematisch formuliert sähe es so aus:
Minimum <= Wert <= Maximum
Leider läß sich das so im Programmcode nicht formulieren und man muß
sich halt mit einer Undverkknüpfung behelfen.
Ob das dann so
(min() <= c) && (c <= max())
oder so
(min() <= c) == (c <= max())
schreibt, läuft letztendlich auf's Gleiche hinaus.
Das C per Default keine Minimum- bzw. Maximumfunktion kennt ist halt
eine Spezialität von C. Aber man kann sich ja mit einem Macro behelfen.
Stefanus F. schrieb:> Erfahrener Entwickler schrieb:> Das ist Quark hochsiebzehn! Einfach nur unfassbarer Blödsinn!>> Warum? Nur weil es mehr Zeilen (im Quelltext) beansprucht?
Du Definierst "ret" und lässt damit den Eindruck erwecken, dass die
Variable zu irgendwas gut sei. Aber nein, die ist eigentlich komplett
überflüssig und verwirrt höchstens weil sie bereits weit vor ihrem
ersten Gebrauch deklariert wird.
Dazu wird die Lesbarkeit durch die vielen überflüssigen Zeilen gegenüber
des Einzeilers NICHT verbessert, das ist das Codeäquivalent von "um den
heißen Brei reden".
Zeno schrieb:> Das C per Default keine Minimum- bzw. Maximumfunktion kennt ist halt> eine Spezialität von C. Aber man kann sich ja mit einem Macro behelfen.
Und die Anwendung des Makros + das Marko selbst ist ganz sicher besser
lesbar als das?:
Johann L. schrieb:> B) Setzt Darstellung negativer Zahlen im 2-er Komplement voraus, was> aber für gängige Architekturen der Fall ist. Portierbarkeit ist also> nur bedingt gegeben.
Casts von signed zu unsigned sind in C eindeutig, da gibt es keine
Implementationsabhängigkeit.
Stefanus F. schrieb:> Und die Anwendung des Makros + das Marko selbst ist ganz sicher besser> lesbar als das?: // assign intervall boundaries> if (a > a + b)> {> upper_bound = a;> lower_bound = a + b;> }> else> {> upper_bound = a + b;> lower_bound = a;> }>> Für mich nicht.
Es hindert Dich keiner dran es so zu machen.
Es gibt halt noch andere Programmiersprachen als C und die kennen sowohl
min als auch max und da reduziert sich das Ganze auf eine lesbare
Programmzeile. Und ja auch dort könnte man es auch mit obigen, dann
zusätzlichen, 10 Zeilen erledigen. Aber warum sollte man dies tun.
Da man eine Minimum-/Maximumfunktion öfter brauchen kann, könnte man das
Ganze ja auch in eine eigene Headerdatei schreiben, so das man es immer
wieder benutzen kann. Es gibt ja noch andere Dinge wo C halt ein bissel
blöd ist und das könnte man dann dort auch gleich mit hinein schreiben.
Johann L. schrieb:> Hier noch eine Lösung, die
...
> B) Setzt Darstellung negativer Zahlen im 2-er Komplement voraus, was> aber für gängige Architekturen der Fall ist. Portierbarkeit ist also> nur bedingt gegeben.
Damit ist eine Portierung auf PDP1 und UNIVAC verunmöglicht.
Jemand schrieb:> Johann L. schrieb:>> B) Setzt Darstellung negativer Zahlen im 2-er Komplement voraus, was>> aber für gängige Architekturen der Fall ist. Portierbarkeit ist also>> nur bedingt gegeben.>> Casts von signed zu unsigned sind in C eindeutig, da gibt es keine> Implementationsabhängigkeit.
Stimmt.
Damit geht es dann auch auf PDP1 und UNIVAC :-)
Johann L. schrieb:> 3) Nur Vergleiche (6), Subtraktion (1) und Negation (2) braucht, also> auch für Controller ohne MUL-Instruktion geeignet ist.
Allerdings geht es hier um einen µC, der eine MUL-Instruktion besitzt,
die dazu noch schneller ist als eine Verzweigung.
Johann L. schrieb:> Jemand schrieb:>> Johann L. schrieb:>>> B) Setzt Darstellung negativer Zahlen im 2-er Komplement voraus, was>>> aber für gängige Architekturen der Fall ist. Portierbarkeit ist also>>> nur bedingt gegeben.>>>> Casts von signed zu unsigned sind in C eindeutig, da gibt es keine>> Implementationsabhängigkeit.>> Stimmt.>> Damit geht es dann auch auf PDP1 und UNIVAC :-)
Dann bin ich beruhigt ;-)
Walter T. schrieb:> Da wird die Hälfte der Aufgabe einfach weggelassen. Die Intervallgrenzen> zu sortieren kostet eventuell mehr Zeit als meine häßliche Variante mit> der Multiplikation.
Eventuell aber auch nicht, oder vielleicht auch doch.
Dein unlesbarer Code dagegen wird dir mit Sicherheit irgendwann auf die
Füsse fallen, alleine schon wegen dessen Anfälligkeit für Overflows bei
der Multiplikation.
P. S. schrieb:> Warum nicht einfach so etwas in dieser Art?
Als jemand, der nicht nach Zeilen bezahlt wird, würde ich das so
schreiben:
zitter_ned_aso schrieb:> Oliver S. schrieb:>> https://godbolt.org/z/pldTZA
Fairerweise sollte man beim Vergleich (im Eröffnungs-Posting stand
Cortex-M3) nicht den 64-Bit-ARM-GCC verwenden.
Ich habe mal alle bisherigen Beispiele zusammengedübelt:
https://godbolt.org/z/fkUCag
Erstaunlich finde ich, das das Kompilat meines Schnellschusses noch am
wenigsten von Compileroptionen (32/64Bit, -O1...-O3) abhängig ist. Und
im Vergleich gar nicht so übel. Einen Unterschied zwischen der
geklammerten und der ungeklammerten Variante konnte ich jetzt nicht
finden.
Ich gehe jetzt mal davon aus, dass es kein Getrolle ist, wenn behauptet
wird, dass das hier
1
return((c>=a&&c<=ab)||
2
(c>=ab&&c<=a));
besser lesbar sei als das hier:
1
return((a-c)*(a+b-c))<=0;
Bei mir ist es übrigens umgekehrt. Vielleicht, weil ich mich in der
Mathematik des 6. Schuljahres mehr zuhause fühle als in jeder
Programmiersprache.
Der für mich aber erstaunlichste Aspekt bleibt: 57 Antworten und eine
heftige Diskussion bei einer Frage, die eher den Anschein einer kleinen
Fingerübung hatte.
Walter T. schrieb:> Gibt es eine schönere Lösung?
Bestimmt gibt es aber eine richtige Lösung.
Wenn die Operanden >16Bit sind, kann die 32Bit-Multiplikation einen
Überlauf ergeben, d.h. das Ergebnis ist Mumpitz und somit auch das
Vorzeichen.
Und auf einer CPU, wo int nur 16 Bit ist, knirscht es schon bei >8Bit
Werten.
Erfahrener Entwickler schrieb:> Yalu X. schrieb:>> Das sehe ich nicht so.>> Das ist halt falsch.>> Einfach lesen:>> Walter T. schrieb:>> Alle drei Variablen können alle Vorzeichen haben.
Das habe ich schon gelesen. Aber wie Johann oben schon angemerkt hat,
ist das Intervall für b<0 leer, d.h. c∈[a,a+b] ist für b<0 immer false.
Das ist aber wohl nicht das, was Walter beabsichtigt.
Aber mittlerweile scheint Konsens darüber zu bestehen, dass nach
Yalu X. schrieb:> c ∈ [a, a+b] ∪ [a+b, a]
gefragt wurde, so dass wir darüber nicht weiter diskutieren müssen.
Johann L. schrieb:> Hier noch eine Lösung, die>> 1) Für alle Werte das richtige Ergebnis liefert.
Wenn mich nicht alles täuscht, stimmen die Ergebnisse für einige
Wertekombinationen, bei denen b=0 oder c=a ist, bspw.
1
a = 0, b = 0, c = -1
2
a = 0, b = -1, c = 0
nicht, wenn int16_t kleiner als int ist.
Ersetzt man die Zeile
Yalu X. schrieb:> Johann L. schrieb:>> Hier noch eine Lösung, die>>>> 1) Für alle Werte das richtige Ergebnis liefert.>> Wenn mich nicht alles täuscht, stimmen die Ergebnisse für einige> Wertekombinationen, bei denen b=0 oder c=a ist, bspw.>
1
> a = 0, b = 0, c = -1
2
> a = 0, b = -1, c = 0
>> nicht, wenn int16_t kleiner als int ist.>> Ersetzt man die Zeile>
1
>return-c_a<=-(uint16_t)b;
>> durch>
1
>return(uint16_t)-c_a<=(uint16_t)-b;
>> scheint das Problem behoben zu sein.
GRMPF. Also noch mehr Casts:
1
return(uint16_t)-c_a<=(uint16_t)-(uint16_t)b;
Damit es bei -b kein signed-Overflow gibt falls b = INT16_MIN = INT_MIN.
Johann L. schrieb:> uint16_t c_a = (uint16_t) c - (uint16_t) a;
Und da muss auch noch ein Cast hin gegen "conversion to 'short unsigned
int' from 'int' may alter its value":
1
uint16_tc_a=(uint16_t)((uint16_t)c-(uint16_t)a);
In Anbetracht der Cast-Orgie würd ich glaub auf Terme mit "a+b"
ausweichen wollen und zum nächstgrößeren Integer (mindestens 2-fache
Bitbreite) übergehen.
Das ist immer noch effizient auf ARM, und ich gehe mal davon aus, dass
das noch so zeitkritisch ist, dass man nicht Zeit für ne handvoll
Instruktionen mehr hätte.
Walter T. schrieb:> Ich gehe jetzt mal davon aus, dass es kein Getrolle ist, wenn behauptet> wird, dass das hier>> return ((c >= a && c <= ab) ||> (c >= ab && c <= a));>> besser lesbar sei als das hier:>> return ((a-c)*(a+b-c)) <= 0;>> Bei mir ist es übrigens umgekehrt.
Ich habe zwar deine Version nach einem anfänglichen "häh?" auch recht
schnell verstanden, aber die Version von Oliver gibt doch viel besser
das wieder, was geprüft werden soll, wodurch auch das "häh?" entfällt
(er hätte vielleicht noch dazu schreiben sollen, dass ab=a+b sein soll):
Der erste Teilausdruck prüft ganz klassisch, ob c in [a, a+b] liegt. Da
die Reihenfolge der Intervallgrenzen keine Rolle spielen soll, wird im
zweiten Teilausdruck zusätzlich geprüft, ob c im Intervall mit
vertauschten Grenzen (also [a+b, a]) liegt. Ist eine der beiden
Prüfungen erfolgreich, ist das Ergebnis true.
Erst wenn Laufzeitoptimierung wichtig wird, ist dein Code etwas besser,
allerdings auf Kosten des nutzbaren Wertebereichs der Argumente.
Yalu X. schrieb:> return -c_a <= - (uint16_t) b;>> durch>> return (uint16_t)-c_a <= (uint16_t)-b;
Über den Integral-Promotion-Faden gestolpert und auf dem
Mixed-Signedness-Comparison-Stein aufgekommen.
Ein Klassiker.
Jemand schrieb:> Stefanus F. schrieb:>> Erfahrener Entwickler schrieb:>> Das ist Quark hochsiebzehn! Einfach nur unfassbarer Blödsinn!>>>> Warum? Nur weil es mehr Zeilen (im Quelltext) beansprucht?>> Du Definierst "ret" und lässt damit den Eindruck erwecken, dass die> Variable zu irgendwas gut sei. Aber nein, die ist eigentlich komplett> überflüssig und verwirrt höchstens weil sie bereits weit vor ihrem> ersten Gebrauch deklariert wird.
Das ist erstens Geschmacks-/Konventionssache ("multiple returns") und
hat zweitens mit dem Thema nur am Rande zu tun. Aber von mir aus auch
so:
Stefanus F. schrieb:> Erfahrener Entwickler schrieb:>> Das ist Quark hochsiebzehn! Einfach nur unfassbarer Blödsinn!>> Warum? Nur weil es mehr Zeilen (im Quelltext) beansprucht?
Ob aus dem Quark durch die Potenzierung mehr oder weniger Quark wird,
hängt davon ab, ob Quark>1 oder Quark<1 ist. Da es keine allgemeine
Definitioon des numerischen Äquivalents von Quark gibt, darf dieses
nach persönlichem Geschmack festgelegt werden. So bleibt bspw. für
Quark=0,1 nach der Potenzierung mit 17 von dem Quark praktisch nichts
mehr übrig, was die Kritik des "erfahrenen Entwicklers" stark
relativiert ;-)
Dennoch kommt der kritisierte Codeabschnitt ziemlich holprig daher:
P. S. schrieb:>> bool isInRange(int a, int b, int c)> {> ...>> // if c out of intervall> if (c > upper_bound || c < lower_bound)> {> ret = false;> }> else> {> ret = true;> }>> return ret;> }
Die Funktion heißt isInRange, geprüft wird aber auf das Gegenteil (c out
of interval). Warum nur? Wäre die Prüfung richtig herum, könnte man sich
sogar den Kommentar sparen.
Da auf das Gegenteil geprüft wird, muss das Ergebnis anschließend noch
negiert werden. Üblicherweise tut man dies mit den !-Operator. Hier wird
aber das Rad neu erfunden, indem die Anweisung
1
ret=!irgendwas;
umständlich als
1
if(irgendwas)
2
{
3
ret=false;
4
}
5
else
6
{
7
ret=true;
8
}
ausgeschrieben wird.
Formuliert man die Prüfung dem Funktionsnamen isInRange entsprechend
1
c>=lower_bound&&c<=upper_bound
braucht man weder den !-Operator, noch dessen Formulierung als
if-else-Anweisung, sondern schreibt einfach
1
ret=(c>=lower_bound&&c<=upper_bound);
Da ret nur ein einziges Mal in der darauffolgenden return-Anweisung
benutzt wird, kann man auch gleich
1
returnc>=lower_bound&&c<=upper_bound;
schreiben.
Wenn man möchte, kann man den linken Teilausdruck umdrehen
1
returnlower_bound<=c&&c<=upper_bound;
um eine gewisse Ähnlichkeit zur mathematischen Schreibweise
1
lower_bound <= c <= upper_bound
herzustellen, die in C in dieser Form zwar legal ist, aber – anders als
bspw. in Python – leider nicht zum gewünschten Ergebnis führt.
Finde ich ja nett, dass der Code gleich so seziert wird und man kann die
Logik der Intervall-Prüfung sicher auch konsistenterweise umdrehen oder
anders implementieren.
Es ging mir einzig und alleine um das Konzept: Also keinen kryptischen
Einzeiler zu verfassen, sondern das Problem mit den "lower" und "upper
boundaries" in zwei Teilprobleme zu zerlegen, deren schrittweise
Abarbeitung auf den ersten Blick nachvollziehbar ist.
Yalu X. schrieb:> Walter T. schrieb:>> Intervall zwischen a und a+b (inklusive)>> [Das] ist das Intervall [a, a+b]. Wenn ich Walter> richtig verstanden habe, will er aber nicht auf>> c ∈ [a, a+b],>> sondern auf>> c ∈ [a, a+b] ∪ [a+b, a]>> prüfen.
Ich bin offen gestanden nicht im Traum darauf gekommen,
Walters Angabe mit den Augen der reinen Mathematik zu
interpretieren. Das macht man ja schließlich bei
Walter T. schrieb:> Hallo zusammen,>> ich habe eine Knobelaufgabe, die mich noch wach hält. Ich will wissen,> ob sich die Variable c im Intervall zwischen a und a+b (inklusive)> befindet. Alle drei Variablen können alle Vorzeichen haben. Der Betrag> von b ist üblicherweise sehr viel kleiner als INT_MAX. Genutzt wird es> mit dem ARM-GCC auf einem Cortex-M3.>> Die Lösung, die mir einfiel, war:>
1
>/** Teste, ob c im Intervall [a; a+b] liegt */
2
>boolisdrin(inta,intb,intc)
3
>{
4
>return((a-c)*(a+b-c))<=0;
5
>}
6
>
>> Gibt es eine schönere Lösung?
Ich verstehe die Aufgabe so, dass geprüft werden soll, ob ein Wert des
Typs T in einem Intervall [L, U] mit L <= U und L,U vom Typ T liegt.
Wird die Vorbedingung für das Intervall nicht eingehalten, ist das ein
Problem des Anwenders. Alternativ kann man diese Vorbedingung natürlich
auch fallen lassen und dem Anwender helfen (s.u.).
Wie bei jeder Arithmetik sind natürlich auch Überläufe zu vermeiden.
Leider kann man das in C nicht allgemein lösen. Deswegen hier ein
Vorschlag in C++:
P. S. schrieb:> Es ging mir einzig und alleine um das Konzept... keinen kryptischen> Einzeiler zu verfassen, sondern das Problem ... in Teilprobleme zu> zerlegen,
Das ist auf jeden Fall eine gute Idee.
Wilhelm M. schrieb:> Ich verstehe die Aufgabe so, dass geprüft werden soll, ob ein Wert des> Typs T in einem Intervall [L, U] mit L <= U und L,U vom Typ T liegt.
Es gibt kein festes Intervall. Die Aufgabenstellung des TO lautet : Es
ist zu prüfen ob die Variable C im Intervall [A, A+B] liegt, wobei die
Variable beliebige positive und negative Werte annehmen können.
Unter dieser Voraussetzung muß das L und das U für jede beliebige
Kombination von A und B bestimmt werden. Erst dann wird Deine Bedingung
gültig. Das Festlegen der Intervallgrenzen ist die Herausforderung
dieser Aufgabe.
Wilhelm M. schrieb:> Leider kann man das in C nicht allgemein lösen.
Selbstverständlich kann man das in C allgemein lösen. Man kann es sogar
so lösen, das alle 3 Variable unterschiedliche Typen haben dürfen. Wie
das geht habe ich aufgezeigt
(Beitrag "Re: C: Prüfen, ob Variable in Intervall").
Die gezeigte Lösung funktioniert 100%ig für jede beliebige Kombination
von a, b, c. Auch bei C gilt z.B. 2.5 > 1.
So langsam wird's lächerlich, wenn man sich die Verrenkungen ansieht die
hier gemacht werden um so ein einfaches Problem sicher zu lösen.
Zeno schrieb:> Wilhelm M. schrieb:>> Ich verstehe die Aufgabe so, dass geprüft werden soll, ob ein Wert des>> Typs T in einem Intervall [L, U] mit L <= U und L,U vom Typ T liegt.>> Es gibt kein festes Intervall. Die Aufgabenstellung des TO lautet : Es> ist zu prüfen ob die Variable C im Intervall [A, A+B] liegt, wobei die> Variable beliebige positive und negative Werte annehmen können.> Unter dieser Voraussetzung muß das L und das U für jede beliebige> Kombination von A und B bestimmt werden. Erst dann wird Deine Bedingung> gültig. Das Festlegen der Intervallgrenzen ist die Herausforderung> dieser Aufgabe.>> Wilhelm M. schrieb:>> Leider kann man das in C nicht allgemein lösen.>> Selbstverständlich kann man das in C allgemein lösen. Man kann es sogar> so lösen, das alle 3 Variable unterschiedliche Typen haben dürfen. Wie> das geht habe ich aufgezeigt> (Beitrag "Re: C: Prüfen, ob Variable in Intervall").> Die gezeigte Lösung funktioniert 100%ig für jede beliebige Kombination> von a, b, c. Auch bei C gilt z.B. 2.5 > 1.
Dann probiere Deinen Ansatz mal für
a = INT_MAX-10;
b = 20;
c = IntMAX - 5;
Zeno schrieb:> Wilhelm M. schrieb:>> Ich verstehe die Aufgabe so, dass geprüft werden soll, ob ein Wert des>> Typs T in einem Intervall [L, U] mit L <= U und L,U vom Typ T liegt.>> Es gibt kein festes Intervall.
Habe ich auch nicht behauptet.
> Die Aufgabenstellung des TO lautet : Es> ist zu prüfen ob die Variable C im Intervall [A, A+B] liegt, wobei die> Variable beliebige positive und negative Werte annehmen können.> Unter dieser Voraussetzung muß das L und das U für jede beliebige> Kombination von A und B bestimmt werden. Erst dann wird Deine Bedingung> gültig. Das Festlegen der Intervallgrenzen ist die Herausforderung> dieser Aufgabe.
Genau. Und das funktioniert bei Deinem Ansatz leider nicht.
>> Wilhelm M. schrieb:>> Leider kann man das in C nicht allgemein lösen.>> Selbstverständlich kann man das in C allgemein lösen.
Nein.
> Man kann es sogar> so lösen, das alle 3 Variable unterschiedliche Typen haben dürfen.
Funktioniert bei Dir aber leider nicht.
> So langsam wird's lächerlich, wenn man sich die Verrenkungen ansieht die> hier gemacht werden um so ein einfaches Problem sicher zu lösen.
Gar nicht. Wegen solchen Ansichten haben sich schon Raumsonden verflogen
...
Zeno schrieb:> Das Festlegen der Intervallgrenzen ist die Herausforderung> dieser Aufgabe.
Ja, sehe ich auch so. Wobei es schon was hat, wenn man keinen Bock hat,
sich um die ganze Aufgabenstellung zu kümmern, einfach einen Teil
wegzulassen und dem Aufrufer aufs Auge zu drücken:
Wilhelm M. schrieb:> Wird die Vorbedingung für das Intervall nicht eingehalten, ist das ein> Problem des Anwenders.
Rolf M. schrieb:> Wilhelm M. schrieb:>> Wird die Vorbedingung für das Intervall nicht eingehalten, ist das ein>> Problem des Anwenders.
... und den Zusatz im Text und im Code hast Du natürlich gelesen, oder?
Zitiere bitte korrekt!
>> Compiliert leider mit den aktuellen Versionen von g++ und clang noch> nicht, daher ungetestet ;)>> Oliver
Wobei solche mehrstelligen Funktion mit gleichen / generischen Typen
sehr problematisch sind. Was bedeutet auf der Aufruferseite etwa:
1
isInRange(1,2,3);
Etwa ist 3 in [1, 2. Oder ist 1 in [2,3].
Der Klassiker eines schlechten Interface-Designs. Gerade das kann man ja
in C++ wesentlich besser machen.
Oliver S. schrieb:> Wilhelm M. schrieb:>> Wobei solche mehrstelligen Funktion mit gleichen / generischen Typen>> sehr problematisch sind.>> Ist ja ok...>>
Und jetzt kannst Du Dir typename T sparen ...
Und man könnte auf std::pair<> verzichten: ein Intervall ist nicht
notwenigerweise ein std::pair<>. Gut, aber lassen wir das ...
Oliver S. schrieb:> Wilhelm M. schrieb:>> Wobei solche mehrstelligen Funktion mit gleichen / generischen Typen>> sehr problematisch sind.>> Ist ja ok...
Das würde ich mit so einer lapidaren Bemerkung keinesfalls abtun.
Die wichtigste Regel im Interface-Design lautet:
Ein Schnittstelle sollte einfach richtig und schwer falsch zu benutzen
sein.
zitter_ned_aso schrieb:> Da die Multiplikation von vielen bemängelt wurde (overflow),> schlage ich> vor nur die Vorzeichen (1, 0, -1) zu multiplizieren.>> Die Idee zur Bestimmung des Vorzeichens habe ich hier geklaut:>> https://stackoverflow.com/questions/1903954/is-there-a-standard-sign-function-signum-sgn-in-c-c>> bool isdrin(int a, int b, int c)> {> int x=(a-c);> int y=(a-c)+b;>> int sign_x = (x>0)-(x<0);> int sign_y=(y>0)-(y<0);>> return (sign_x*sign_y) <= 0;> }
Ohne Multiplikation dann die Vorzeichen (MSB) von (a-c) und (a-c)+b XOR
verknüpfen ;)
im Allgemeinen bin ich aber hierfür:
P. S. schrieb:> Also keinen kryptischen> Einzeiler zu verfassen, sondern das Problem mit den "lower" und "upper> boundaries" in zwei Teilprobleme zu zerlegen, deren schrittweise> Abarbeitung auf den ersten Blick nachvollziehbar ist.
Lukas schrieb:> zitter_ned_aso schrieb:>> Da die Multiplikation von vielen bemängelt wurde (overflow),>> schlage ich>> vor nur die Vorzeichen (1, 0, -1) zu multiplizieren.>>>> Die Idee zur Bestimmung des Vorzeichens habe ich hier geklaut:>>>>> https://stackoverflow.com/questions/1903954/is-there-a-standard-sign-function-signum-sgn-in-c-c>>>> bool isdrin(int a, int b, int c)>> {>> int x=(a-c);>> int y=(a-c)+b;>>>> int sign_x = (x>0)-(x<0);>> int sign_y=(y>0)-(y<0);>>>> return (sign_x*sign_y) <= 0;>> }>> Ohne Multiplikation dann die Vorzeichen (MSB) von (a-c) und (a-c)+b XOR> verknüpfen ;)
Das ist mal wieder typischer Code von Leuten, die schlauer sein wollen,
als der Compiler ;-) Übrigens auch der ursprüngliche Ansatz.
M.E. immer eine schlechte Idee. Optimierungen sind die vornehmste
Aufgabe eines Compilers. Man schreibt es verständlich hin und stattet
den Compiler mit soviel Informationen (Datentypen) aus, wie möglich.
Alles andere ist seine Aufgabe. Und die Compiler sind gut darin.
Wilhelm M. schrieb:> M.E. immer eine schlechte Idee. Optimierungen sind die vornehmste> Aufgabe eines Compilers.
Deswegen auch der ";)" und der Absatz danach
Wilhelm M. schrieb:> Leider kann man das in C nicht allgemein lösen. Deswegen hier ein> Vorschlag in C++:>> ...> <viele Zeilen Code>> ...
Man denkt eigentlich, bei so viel Code für so eine einfache Funktion
sollte das Beispiel wenigstens vollständig sein. Mal abgesehen davon,
dass da C++-Erweiterungen von zukünftigen Standards verwendet werden,
die noch lange nicht von jedem Compiler unterstützt werden, gibt es noch
jede Menge nichtdeklarierter Symbole:
Was ist z.B. containing_type_t?
Woher kommt der Namespace etl? Im Netz findet man gleich zwei ETLs,
nämlich
https://github.com/ETLCPP/etl und
https://www.etlcpp.com/function_tutorial.html
von denen aber keine den Typ enclosing_t definiert.
Ich schätze, es handelt sich bei all diesen fehlenden Symbolen um Dinge,
die du dir selbst zusammengedengelt hast, aber die Allgemeinheit nicht
daran teilhaben lassen möchtest. Das ist natürlich dein gutes Recht,
aber welchen Nutzen sollen solche Beiträge haben, außer den anderen vor
Augen zu führen, welch toller C++-Hecht du bist?
Wilhelm M. schrieb:>> Man kann es sogar>> so lösen, das alle 3 Variable unterschiedliche Typen haben dürfen.>> Funktioniert bei Dir aber leider nicht.
Natürlich funktioniert das! Probier diesen Code, dann wirst Du sehen das
es funktioniert
1
/*TestRange.c*/
2
#include<stdio.h>
3
#include<stdlib.h>
4
5
#define max(a,b) (((a) > (b)) ? (a) : (b))
6
#define min(a,b) (((a) < (b)) ? (a) : (b))
7
8
unsignedcharisdrin(doublea,doubleb,doublec)
9
{
10
return(min(a,a+b)<=c&&c<=max(a,a+b));
11
}
12
13
intmain(void){
14
inta,i;
15
doubleb,c,j,k;
16
a=-2;
17
b=-1.2;
18
c=-2.1;
19
j=min(a,a+b);
20
k=max(a,a+b);
21
i=isdrin(a,b,c);
22
printf("min %f\nmax %f\ndrin %d\n",j,k,i);
23
returnEXIT_SUCCESS;
24
}
Der Code erzeugt bei mir genau diese Ausgabe:
min -3.200000
max -2.000000
drin 1
Wilhelm M. schrieb:> Dann probiere Deinen Ansatz mal für>> a = INT_MAX-10;> b = 20;> c = IntMAX - 5;
Klar funktioniert der! Kommt genau das raus:
min -2147483639.000000
max 2147483637.000000
drin 1
Entscheident ist, das man bei der Vergleichsfunktion einen Datetyp
benutzt der einen größeren Bereich hat. Wenn man für isdrin() int als
Datentyp bestimmt kommt schon Blödsin raus. Das ist aber auch logisch,
das das nicht funktionieren kann, wenn die Variablen als int deklariert
sind. Da funktioniert allein schon der Ansatz des TO nicht die Grenzen
mit a+b festzulegen. Im Umkehrschluß heißt das bevor überhaupt geprüft
wird ob c im Intervall liegt muß geprüft werden ob die Intervallgrenzen,
also in diesem Fall a+b, innerhalb von int liegen. Du kannst
normalerweise auch nicht a INT_MAX + 10 zuweisen. Normalerweise! Aber
bei C gehen auch solche Schweinereien wie ich gerade feststellen mußte.
Der Kompiler meckert zwar den Integeroverflow an, kompiliert aber das
Ganze ohne Fehler, das Programm läuft durch und liefert sogar das
richtige Ergebnis.
Das
1
a=INT_MAX+10;
2
b=20;
3
c=INT_MAX+9;
als auch das
1
a=INT_MAX+10;
2
b=20;
3
c=INT_MAX+11;
a und b habe ich für diesen Test ebenfalls mal als int definiert.
Für Ersteres kommt 0 raus da die Grenzen (INT_MAX+10, INT_MAX_30) sind.
Im zweiten Fall kommt dementsprechend 1 heraus.
Das gibt es in keiner anderen Programmiersprache. Da lehnt der Kompiler
schon das Kompilieren ab und das ist auch gut so.
Wilhelm M. schrieb:> Wegen solchen Ansichten haben sich schon Raumsonden verflogen
Bei Deinem Code schon, weil er vorbeigeflogen ist bevor er mit rechnen
fertig war. :-)
Wilhelm M. schrieb:> Wobei solche mehrstelligen Funktion mit gleichen / generischen Typen> sehr problematisch sind. Was bedeutet auf der Aufruferseite etwa:> isInRange(1, 2, 3);
Was darf/sollte man in C noch alles nicht machen? Wieder mal typische
C-Reglementierung, man schreibt gültigen Code darf man aber nicht, dafür
werden aber Dinge toleriert wo jeder anständige Compiler einem den
Quelltext um die Ohren haut.
Und um noch mal auf das Thema des TO zurückzukommen. Das Entscheidende
ist zu bestimmen was die untere und was die obere Grenze des
Wertebereichs ist. Das kann man so machen wie hier
Beitrag "Re: C: Prüfen, ob Variable in Intervall" beschrieben oder
eben so wie ich das gemacht habe.
Ob Dein Ansatz funktioniert kann ich nicht beurteilen, weil ich es nicht
geprüft habe. Ich bekomme von Deinem Ansatz für so ein triviales Problem
Augenkrebs und andererseits ist er für C ungeeignet weil halt C++. Bei
Überschreitungen des Wertebereichs brauche ich eigentlich gar nicht erst
mit Rechnen anfangen, weil wenn es überhaaupt kompiliert nur Schnulli
raus kommt. Wenn bei der Aufgabe des TO schon bei der Berechnung der
Intervallgrenzen der Wertebereich überschritten wird, dann ist schon im
Vorfeld was gewaltig schief gelaufen und da erübrigt sich weitere
Berechnung.
Yalu X. schrieb:> ....> Ich schätze, es handelt sich bei all diesen fehlenden Symbolen um Dinge,> die du dir selbst zusammengedengelt hast, aber die Allgemeinheit nicht> daran teilhaben lassen möchtest. Das ist natürlich dein gutes Recht,> aber welchen Nutzen sollen solche Beiträge haben, außer den anderen vor> Augen zu führen, welch toller C++-Hecht du bist?
Oh hier hat aber ein Experte ganau untersucht.
Mit anderen Worten ich hätte es als gar nicht nachvollziehen können,
weil kein C++ Standard - Richtig?
Zeno schrieb:> Mit anderen Worten ich hätte es als gar nicht nachvollziehen können,> weil kein C++ Standard - Richtig?
Ich schätze, mit clang und der Option -std=c++2a wäre das Ganze
kompilierbar, wenn all die fehlenden Dinge verfügbar wären, von denen
niemand außer Wilhelm weiß, wo sie herstammen. Mit dem aktuellen
GCC-Release (9.1) hast du jedoch kein Chance.
Yalu X. schrieb:> Wilhelm M. schrieb:>> Leider kann man das in C nicht allgemein lösen. Deswegen hier ein>> Vorschlag in C++:>>>> ...>> <viele Zeilen Code>>> ...>> Man denkt eigentlich, bei so viel Code für so eine einfache Funktion> sollte das Beispiel wenigstens vollständig sein. Mal abgesehen davon,> dass da C++-Erweiterungen von zukünftigen Standards verwendet werden,> die noch lange nicht von jedem Compiler unterstützt werden, gibt es noch> jede Menge nichtdeklarierter Symbole:>> Was ist z.B. containing_type_t?
Was könnte denn wohl die Meta-Funktion bedeuten?
> Ich schätze, es handelt sich bei all diesen fehlenden Symbolen um Dinge,> die du dir selbst zusammengedengelt hast, aber die Allgemeinheit nicht> daran teilhaben lassen möchtest.
Auch die von Dir gelisteten Bibliotheken, die den namespace etl
verwenden, sind "zusammengedengelt", denn auch sie sind ja in keinster
Weise standardisiert.
> Das ist natürlich dein gutes Recht,> aber welchen Nutzen sollen solche Beiträge haben, außer den anderen vor> Augen zu führen, welch toller C++-Hecht du bist?
Nur weil Du es nicht verstehst, muss ich ja kein C++Hecht sein.
Yalu X. schrieb:> ....> Ich schätze, es handelt sich bei all diesen fehlenden Symbolen um Dinge,> die du dir selbst zusammengedengelt hast, aber die Allgemeinheit nicht> daran teilhaben lassen möchtest. Das ist natürlich dein gutes Recht,> aber welchen Nutzen sollen solche Beiträge haben, außer den anderen vor> Augen zu führen, welch toller C++-Hecht du bist?
Oh hier hat aber ein Experte ganau untersucht.
Mit anderen Worten ich hätte es als gar nicht nachvollziehen können,
weil kein C++ Standard - Richtig?
Yalu X. schrieb:> Zeno schrieb:>> Mit anderen Worten ich hätte es als gar nicht nachvollziehen können,>> weil kein C++ Standard - Richtig?>> Ich schätze, mit clang und der Option -std=c++2a wäre das Ganze> kompilierbar, wenn all die fehlenden Dinge verfügbar wären, von denen> niemand außer Wilhelm weiß, wo sie herstammen. Mit dem aktuellen> GCC-Release (9.1) hast du jedoch kein Chance.
Danke für die Rückinfo.
Das sind mir schon die Richtigen erst tönen was man nicht darf/sollte
Wilhelm M. schrieb:> Wobei solche mehrstelligen Funktion mit gleichen / generischen Typen> sehr problematisch sind. Was bedeutet auf der Aufruferseite etwa:> isInRange(1, 2, 3);
daraus das schlußfolgern
Wilhelm M. schrieb:> Ein Schnittstelle sollte einfach richtig und schwer falsch zu benutzen> sein.
und dann Code abliefern der von der Allgemeinheit nicht nachvollzogen
werden kann.
Zeno schrieb:> Yalu X. schrieb:>> ....>> Ich schätze, es handelt sich bei all diesen fehlenden Symbolen um Dinge,>> die du dir selbst zusammengedengelt hast, aber die Allgemeinheit nicht>> daran teilhaben lassen möchtest. Das ist natürlich dein gutes Recht,>> aber welchen Nutzen sollen solche Beiträge haben, außer den anderen vor>> Augen zu führen, welch toller C++-Hecht du bist?>> Oh hier hat aber ein Experte ganau untersucht.> Mit anderen Worten ich hätte es als gar nicht nachvollziehen können,> weil kein C++ Standard - Richtig?
Bis auf die Requirements ist alles aktueller C++-Standard. Aber die
Requirements kannst Du auch weglassen. Dann ist nur die Fehlermeldung
nicht mehr so schön.
Hat denn nach so viel tollen Vorschlägen mal jemand eine Wertung zu
bieten, was (unabhängig von der Lesbarkeit des Codes) die beste Lösung
ist?
Wobei sich für "beste" m. E. zwei Kriterien ergeben, Size und Speed
(wobei das auch abhängig vom Compiler sein mag, das wird man sehen).
Es wäre interessant, ob der teilweise kryptische Code da jedenfalls
einen Vorteil bietet. Oder ob gut lesbarer Code auch der bessere ist.
Oder ob alles egal ist, das wäre dann ebenfalls ein Vorteil für den
besser lesbaren Code.
Wilhelm M. schrieb:> Auch die von Dir gelisteten Bibliotheken, die den namespace etl> verwenden, sind "zusammengedengelt", denn auch sie sind ja in keinster> Weise standardisiert.
Es gibt aber einen signifikanten Unterschied: Diese Bibliotheken sind
für jeden verfügbar, deine nicht. Man kann sich zwar selber seine
eigenen schreiben, dann braucht man aber deine Beiträge nicht.
Ich habe übrigens ein ganz tolles Programm geschrieben, das, obwohl sehr
kurz, sämtliche Probleme dieser Welt löst:
solve_problem.c:
Ist das nicht cool?
Die Implementierung von solve_problem habe ich aus Platzgründen mal
weggelassen. Jeder, der halbwegs C oder C++ programmieren kann, sollte
das problemlos selber hinbekommen ;-)
Merkst du was?
Also:
Tu den Forenteilnehmern doch in Zukunft einen Gefallen und deklarariere
deine Code-Beispiele, wenn du sie schon unbedingt veröffentlichen musst,
wenigstens als Anwendungsbeispiele für deine Closed-Source-Bibliotheken.
Zeno schrieb:
Du hattest zuerst diese Lösung:
Beitrag "Re: C: Prüfen, ob Variable in Intervall"
auf die ich mich bezogen habe. Auch Du hast extra nochmal in diesem
Beitrag
Beitrag "Re: C: Prüfen, ob Variable in Intervall"
darauf hingewiesen.
Die hat sich dann nachher schwupp-die-wupp zu dem hier
> /*TestRange.c*/> #include <stdio.h>> #include <stdlib.h>>> #define max(a,b) (((a) > (b)) ? (a) : (b))> #define min(a,b) (((a) < (b)) ? (a) : (b))>> unsigned char isdrin(double a, double b, double c)> {> return (min(a, a+b)<=c && c<= max(a, a+b));> }
verändert (toller Typ für die Funktion :-))
Sorry, ich kann Deinen Code nicht compilieren, weil mein restliches
Programm durch die Präprozessor-Makros zerschossen wird.
> Wilhelm M. schrieb:>> Dann probiere Deinen Ansatz mal für>>>> a = INT_MAX-10;>> b = 20;>> c = IntMAX - 5;
Und Deine ursprüngliche Lösung arbeitet nicht korrekt.
min = 2147483637
max = -2147483639
c = 2147483642
c ist zwar größer min, aber nicht kleiner max --> false
Wilhelm M. schrieb:> Und Deine ursprüngliche Lösung arbeitet nicht korrekt.>> min = 2147483637> max = -2147483639> c = 2147483642>> c ist zwar größer min, aber nicht kleiner max --> false
Wenn man als Vorgabe "min" kleiner macht als "max", muss false
rauskommen!
Zeno schrieb:> Ich bekomme von Deinem Ansatz für so ein triviales Problem> Augenkrebs
das tut mich echt leid.
> und andererseits ist er für C ungeeignet weil halt C++.
Das hatte ich ja dazu gesagt und auch begründet.
> Bei> Überschreitungen des Wertebereichs brauche ich eigentlich gar nicht erst> mit Rechnen anfangen, weil wenn es überhaaupt kompiliert
ja, der Schrott kompiliert...
> nur Schnulli> raus kommt.
Ja, und es kommt Schnulli bei raus.
> Wenn bei der Aufgabe des TO schon bei der Berechnung der> Intervallgrenzen der Wertebereich überschritten wird, dann ist schon im> Vorfeld was gewaltig schief gelaufen und da erübrigt sich weitere> Berechnung.
Jawoll, Du hast es erfasst. Und jetzt denk mal scharf darüber nach,
welche Problem ich oben u.a. adressiert habe.
Dieter R. schrieb:> Hat denn nach so viel tollen Vorschlägen mal jemand eine Wertung zu> bieten, was (unabhängig von der Lesbarkeit des Codes) die beste Lösung> ist?>> Wobei sich für "beste" m. E. zwei Kriterien ergeben, Size und Speed> (wobei das auch abhängig vom Compiler sein mag, das wird man sehen).>> Es wäre interessant, ob der teilweise kryptische Code da jedenfalls> einen Vorteil bietet. Oder ob gut lesbarer Code auch der bessere ist.> Oder ob alles egal ist, das wäre dann ebenfalls ein Vorteil für den> besser lesbaren Code.
Welcher Teil von
Dieter R. schrieb:> Hat denn nach so viel tollen Vorschlägen mal jemand eine Wertung zu> bieten, was (unabhängig von der Lesbarkeit des Codes) die beste Lösung> ist?>> Wobei sich für "beste" m. E. zwei Kriterien ergeben, Size und Speed> (wobei das auch abhängig vom Compiler sein mag, das wird man sehen).
Korrektheit ist auch nicht ganz unerheblich ;-)
Wilhelm M. schrieb:> Und Deine ursprüngliche Lösung arbeitet nicht korrekt.>> min = 2147483637> max = -2147483639> c = 2147483642>> c ist zwar größer min, aber nicht kleiner max --> false
Kannste lesen? Nö offensichtlich nicht. Hatte ja geschrieben, das wenn
man alles int declariert Deine Zahlenbeispiele nicht funktionieren.
Zeno schrieb:> a und b habe ich für diesen Test ebenfalls mal als int definiert.Wilhelm M. schrieb:> Die hat sich dann nachher schwupp-die-wupp zu dem hier
Nix schwupp-die-wupp! In der Funktion hatte ich das im Nachgang auf
double geändert, um nachprüfen zu können, das mein Ansatz sowohl mit int
als auch double bzw. gemischt funktioniert. Aber mitdenken ist da auch
nicht gerade Deine Stärke obwohl Du es von den anderen verlangst.
Wilhelm M. schrieb:> Sorry, ich kann Deinen Code nicht compilieren, weil mein restliches> Programm durch die Präprozessor-Makros zerschossen wird.
Jetzt wundert mich nichts mehr. Du bekommst noch nicht mal so pobkige
Präprozessor-Makros zu laufen? Dann solltest Du dringens Deine
Entwiklungsumgebung prüfen. Kein Wunder das Du dann so kryptischen Kram
schreiben mußt.
Wilhelm M. schrieb:> Und Du möchtest tatsächlich immer mit double rechnen? Hattest nicht> gerade Du die Bemerkung mit Performance, etc. gebracht.
Wo habe ich was von Performance geschrieben? Du kannst wirklich nicht
lesen. Nenne mir doch einfach den Post wo ich dieses Wort benutzt habe.
Im übrigen wird mein double immer noch schneller sein als Dein
kryptischer Code den keiner versteht und den niemand nach vollziehen
kann weil Du eigenen Kram benutzt den keiner hat.
Hugo E. schrieb:> Wenn man als Vorgabe "min" kleiner macht als "max", muss false> rauskommen!
Hatte ich ja auch geschrieben, das Blödsinn rauskommt wenn man alles als
int deklariert und anschließend den Variablen Werte zuweist die nicht im
Bereich von int sind. Es ist halt schlecht wenn bei C so was durch geht.
Jeder andere Compiler würde einen den Quelltext um die Ohren hauen. Aber
bei C geht ja auch so etwas:
1
inta=2.99
Gibt zwar ne Warning aber der Compiler schluckt es und interpretiert das
Ganze als 2. Gibt es auch in keiner anderen typisierten
Programmiersprache.
Zeno schrieb:> Wilhelm M. schrieb:>> Und Deine ursprüngliche Lösung arbeitet nicht korrekt.>>>> min = 2147483637>> max = -2147483639>> c = 2147483642>>>> c ist zwar größer min, aber nicht kleiner max --> false>> Kannste lesen? Nö offensichtlich nicht. Hatte ja geschrieben, das wenn> man alles int declariert Deine Zahlenbeispiele nicht funktionieren.
Ach, jetzt liegt es an meine Werten, dass Dein Code nicht funktioniert.
Na super!
>> Zeno schrieb:>> a und b habe ich für diesen Test ebenfalls mal als int definiert.>> Wilhelm M. schrieb:>> Die hat sich dann nachher schwupp-die-wupp zu dem hier>> Nix schwupp-die-wupp! In der Funktion hatte ich das im Nachgang auf> double geändert,
Genau, im Nachgang. Ich konnte mich ja wohl dann nur auf Dein
int-Beispile bezieg
>um nachprüfen zu können, das mein Ansatz sowohl mit int> als auch double bzw. gemischt funktioniert. Aber mitdenken ist da auch> nicht gerade Deine Stärke obwohl Du es von den anderen verlangst.>> Wilhelm M. schrieb:>> Sorry, ich kann Deinen Code nicht compilieren, weil mein restliches>> Programm durch die Präprozessor-Makros zerschossen wird.> Jetzt wundert mich nichts mehr. Du bekommst noch nicht mal so pobkige> Präprozessor-Makros zu laufen?
Das hast Du nicht wirklich ernst genommen, oder? Oh man ...
> Dann solltest Du dringens Deine> Entwiklungsumgebung prüfen. Kein Wunder das Du dann so kryptischen Kram> schreiben mußt.
Das hat gar nichts mit der IDE zu zun.
>> Wilhelm M. schrieb:>> Und Du möchtest tatsächlich immer mit double rechnen? Hattest nicht>> gerade Du die Bemerkung mit Performance, etc. gebracht.> Wo habe ich was von Performance geschrieben?
Dein Antwort auf die Raumsonde, Du erinnerst Dich bestimmt.
> Du kannst wirklich nicht> lesen. Nenne mir doch einfach den Post wo ich dieses Wort benutzt habe.
s.o.
> Im übrigen wird mein double immer noch schneller sein als Dein> kryptischer Code den keiner versteht und den niemand nach vollziehen> kann weil Du eigenen Kram benutzt den keiner hat.
Ich glaube, Du hast echt keine Ahnung. Kannst Du Laufzeitberechnungen
von Compilezeitberechnungen unterscheiden?
> Hugo E. schrieb:>> Wenn man als Vorgabe "min" kleiner macht als "max", muss false>> rauskommen!>> Hatte ich ja auch geschrieben, das Blödsinn rauskommt wenn man alles als> int deklariert und anschließend den Variablen Werte zuweist die nicht im> Bereich von int sind.
Na, dann wir uns ja einige, dass Dein Code Schrott ist.
>Es ist halt schlecht wenn bei C so was durch geht.
Das sage ich doch. In C kann man es nicht besser machen, in C++ schon.
> Jeder andere Compiler würde einen den Quelltext um die Ohren hauen. Aber> bei C geht ja auch so etwas:>
1
inta=2.99
> Gibt zwar ne Warning aber der Compiler schluckt es und interpretiert das> Ganze als 2. Gibt es auch in keiner anderen typisierten> Programmiersprache.
Wir sprechen nun aber von C bzw. in meinem Fall von C++.
Hallo, für mich als "Nicht-C'ler" trotzdem sehr interessant, hier mit zu
lesen.
Der TO hat sich also eine Formel "ausgedacht" und hingeschrieben. Die
Frage, ob das eleganter geht, hat mehr oder weniger üppigen Code hervor
gebracht...aber für mich ist das alles nicht eleganter! Unter der
Annahme, dass sich die 3 Werte im legitimen Wertebereich des Controllers
befinden, sollte ein Compiler entweder "meckern" oder die Formel
umsetzen...mit gültigem Ergebnis! Aber offensichtlich haben hier einige
Leute die Erfahrung gemacht, dass ihr Compiler bei Rechenaufgaben öfter
schon mal Mist macht! Anders kann ich mir die Code-Beispiele mit den
mehr oder weniger umfangreichen Bereichsüberprüfungen nicht erklären.
Wenn die Frage nach "eleganter" allerdings auf das Problem der
Bereichsüberprüfung (x in [a,b]) zielt, so ist die Antwort doch
eindeutig "JA". Wenn, wie gesagt, alle Werte aus dem zulässigen
Wertebereich stammen, dann überprüfe ich schlicht, ob 1. x<=b und 2. ob
x>=a und fertig. Das werden in C selbst als Funktion mit den Parametern
kaum mehr als 3 Zeilen sein.
Gruß Rainer
Zeno schrieb:> Wilhelm M. schrieb:>> Und Du möchtest tatsächlich immer mit double rechnen? Hattest nicht>> gerade Du die Bemerkung mit Performance, etc. gebracht.> Wo habe ich was von Performance geschrieben? Du kannst wirklich nicht> lesen. Nenne mir doch einfach den Post wo ich dieses Wort benutzt habe.> Im übrigen wird mein double immer noch schneller sein als Dein> kryptischer Code den keiner versteht und den niemand nach vollziehen> kann weil Du eigenen Kram benutzt den keiner hat.
Das beweist recht eindrucksvoll dass die meisten Leute keine Ahnung
haben was hinter ihrem Code tatsächlich an Maschinenbefehlen rausfällt.
Mir ist aktuell kein Cortex M3 bekannt der eine Double-Precision FP
enthält.
Das heißt dein Code-Beispiel mit den doubles erzeugt Aufrufe nach;
- __adddf3
- __aeabi_dcmpgt
- __aeabi_dcmple
- __aeabi_dcmplt und
- __aeabi_dcmpge
Das sind so in etwa ~220 Zeilen Assembler. Dazu kommt dann nochmal gut
ein Dutzend moves für die Herumschieberei zwischen den Aufrufen.
Willhelms Code hab ich jetzt nicht ausprobiert aber nach Gefühl schätz
dass er rund 10x schneller sein wird...
/edit
Sagen wir lieber 10x kleiner. Schneller trau ich mich in Zeiten von
Caches nicht mehr so direkt sagen...
Vincent H. schrieb:> Sagen wir lieber 10x kleiner. Schneller trau ich mich in Zeiten von> Caches nicht mehr so direkt sagen...
Der Cortex-M3, um den es hier geht, hat keinen Cache, wenn nicht der
Chip-Hersteller in Eigenregie einen angebaut hat.
Deswgen kann man bei dieser CPU noch recht gut die Taktzyklen für ein
Stück Code zählen. Ein paar Befehle (insbesondere die Branch-Befehle)
haben zwar auf Grund von Pipeline-Effekten variable Ausführungszeiten,
wenn diese Befehle im Code aber nicht zu oft vorkommen, kann man die
Gesamtzyklenzahl dennoch ganz gut abschätzen.
Voraussetzung für das Zyklenzählen ist natürlich, dass man den Code
erst einmal durch den Compiler bekommt ;-)
Wilhelm M. schrieb:> Ach, jetzt liegt es an meine Werten, dass Dein Code nicht funktioniert.> Na super!
Ich hab's begründet warum es nicht funktioniert. Hier noch mal für Dich
zum Mitschreiben : Bereichsüberschreitung!
Ich wiederhole es gern auch noch mal, normalerweise gehört es sich das
das Compiler das gar nicht ers compiliert wenn er zur Compilezeit wegis
das es eine Bereichsüberschreitung ist und 2. sollte im Normalfall das
Programm gar nicht weiterrechnen wenn es zu einer Bereichsüberschreitung
kommt.
Wilhelm M. schrieb:> Das hast Du nicht wirklich ernst genommen, oder? Oh man ...
Dann schreib nicht so'nen Käse, dann gibt es auch keine entsprechenden
Antworten.
Wilhelm M. schrieb:> Na, dann wir uns ja einige, dass Dein Code Schrott ist.
Wir sind uns überhaupt nicht einig. Mein Code funktioniert sofern sich
die Variablen im zulässigen Bereich bewegen. Ob Dein Code funktioniert
ist ja aus schon mehrfach (nicht von mir) genannten Gründen nicht
nachvollziehbar. Und bis Du das Gegenteil beweist funktioniert er erst
mal nicht.
Wilhelm M. schrieb:> Wir sprechen nun aber von C bzw. in meinem Fall von C++.
Das ist wohl war, aber man wird's wohl benennen dürfen das dies Mist ist
und das genau das ander Programmiersprachen besser können.
Vincent H. schrieb:> Das beweist recht eindrucksvoll dass die meisten Leute keine Ahnung> haben was hinter ihrem Code tatsächlich an Maschinenbefehlen rausfällt.> Mir ist aktuell kein Cortex M3 bekannt der eine Double-Precision FP> enthält.
Du hast es auch nicht gerafft. Mein erster Post arbeitet ausschließlich
mit int und der funktioniert auch solange die Bereichgrenzen für int
nicht überschritten werden.
Das mit dem double hatte ich lediglich mal gemacht um zu prüfen ob es
auch mit double bzw. gemischten Variablen funktioniert. Ich habe es dann
vor dem Posten des Testprogrammes nicht rückgängig gemacht und so
belassen wie ich getestet habe.
Wer der Meinung ist das er nicht sicher stellen kann, das seine
Variablen die Bereichsgrenzen nicht überschreiten muß dies zuvor
abprüfen.
Ich habe auch nicht behauptet, das ich es auf einem µC getestet habe
(das sieht man auch bei meinem 2.Quelltextpost). Es ging mir lediglich
um die Machbarkeit.
Dann poste doch mal eigenen Code wie Du es machen würdest.
Es steht Dir auch alle Codevorschläge auf Geschwindigkeit zu prüfen und
die Ergebnisse hier zu präsentieren. Behaupten kann man viel.
a_zip schrieb:> Wenn, wie gesagt, alle Werte aus dem zulässigen> Wertebereich stammen, dann überprüfe ich schlicht, ob 1. x<=b und 2. ob> x>=a und fertig. Das werden in C selbst als Funktion mit den Parametern> kaum mehr als 3 Zeilen sein.
Es sind auch nicht mehr Zeilen - habe ich doch bewiesen. Das "Problem"
hier ist nur, das die Grenzen a und b variabel sind und sich die Grenze
b aus der Addition von a und b berechnet. Wobei a und b beliebige Werte
aus dem Integerbereich sind. Damit ist nicht mehr gewährleistet das a <=
b ist, was Vorausetzung für Dein 1. und 2. ist.
a_zip schrieb:> Unter der Annahme, dass sich die 3 Werte im legitimen> Wertebereich des Controllers befinden, sollte ein> Compiler entweder "meckern" oder die Formel umsetzen...> mit gültigem Ergebnis!
Das ist m.M.n. falsch.
Das einzige ernsthafte TECHNISCHE Problem mit Walters
Idee besteht ja gerade darin, dass auch die ZWISCHEN-
ERGEBNISSE im gültigen Bereich bleiben müssen!
Seine Methode funktioniert also NICHT für beliebige
zulässige Variablenbelegungen, weil trotzdem noch
unzulässige Zwischenergebnisse entstehen können.
Genau das ist ja der Kern aller Probleme mit Integer-
Überlauf!
Walter T. schrieb:> Ich lese das jetzt seit zwei Tagen mit wachsender> Verzweiflung mit. Kommt da noch ein "April April -> wir haben uns alle abgesprochen!" ?> Bitte?
???
Was habe ich denn jetzt wieder falsch gemacht?
Egon D. schrieb:> Seine Methode funktioniert also NICHT für beliebige> zulässige Variablenbelegungen, weil trotzdem noch> unzulässige Zwischenergebnisse entstehen können.
Das Hauptproblem ist erst mal die Berechnung des 2. Grenzwertes der sich
lt. Walter aus der Summe a+b ergibt. Hier ist generell erst mal zu
prüfen ob es eine Überschreitung des Integerbereiches gibt, es sei denn
man weis ganz genau das dies nicht passieren kann. Das wäre ja bei
Wilhelms Beispiel genau der Fall.
Die Crux an der Sache ist das es der Compiler zur Compilezeit nicht
feststellen kann, zumindest dann nicht wenn die Variable dynamisch mit
Werten belegt werden.
Zur Runtime bleibt dann eigentlich nur das die Variable auf Einhaltung
des Wertebereichs geprüft werden, man sich mit einem Programmabsturz
begnügt oder das Ganze via Exceptionhandling abfängt. Letzteres gibt es
bei C nicht. Das zweite umschifft der Compiler wie auch immer, bleibt
also nur Möglichkeit 1.
Zeno schrieb:> Egon D. schrieb:>> Seine Methode funktioniert also NICHT für beliebige>> zulässige Variablenbelegungen, weil trotzdem noch>> unzulässige Zwischenergebnisse entstehen können.>> Das Hauptproblem ist erst mal die Berechnung des 2. Grenzwertes der sich> lt. Walter aus der Summe a+b ergibt. Hier ist generell erst mal zu> prüfen ob es eine Überschreitung des Integerbereiches gibt, es sei denn> man weis ganz genau das dies nicht passieren kann. Das wäre ja bei> Wilhelms Beispiel genau der Fall.
So langsam beginnst Du zu verstehen. Schau nochmal ganz genau hin.
Wilhelm M. schrieb:> So langsam beginnst Du zu verstehen. Schau nochmal ganz genau hin.
Meinst Du! Nö da muß ich nicht noch mal hinschauen.
Ist halt doof wenn die Werkzeuge, in dem Fall der Compiler, nicht so
funktionieren wie man es erwartet bzw. von anderen gewohnt ist.
Walter T. schrieb:> return ((a-c)*(a+b-c))
Na klar kann der Compiler nicht wissen, ob die Multiplikation einen
Überlauf ergibt. Also wird die Zeile compiliert und bei einigen Werten
der inneren Klammern stürzt das Programm irgendwie ab. Das erfordert
also eine durchgehende Bereichsüberprüfung und Fehlerbehandlung und ist
daher überhaupt nicht elegant und dem eigentlichen Problem auch nicht
angemessen! Da hat sich der TO eine überflüssige, schlaflose Nacht
gegönnt...
Gruß Rainer
Egon D. schrieb:> Walter T. schrieb:>> Was habe ich denn jetzt wieder falsch gemacht?
Naja, für eine Aufgabe aus den ersten Stunden der Anfängervorlesung ist
der Thread ganz schön lang geworden. Darauf wollte Walter T. wohl
hinweisen.
Trotzdem warte ich immer noch auf die Aufklärung, welche Lösung
(vorausgesetzt, sie funktioniert überhaupt im Sinne von mathematischer
Korrektheit) denn die effektivste ist.
Zeno schrieb:> Das Hauptproblem ist erst mal die Berechnung des 2. Grenzwertes der sich> lt. Walter aus der Summe a+b ergibt. Hier ist generell erst mal zu> prüfen ob es eine Überschreitung des Integerbereiches gibt, es sei denn> man weis ganz genau das dies nicht passieren kann.
Eben. So etwas weiß man i.d.R. in Bezug auf die konkrete Anwendung. Bei
der Definition eines allgemeinen intervall-checkers eher nicht.
Die meisten Zahlen sind klein.
Wenn Ihr in RL auch so lahm seid, so ein simples Problen zu lösen, dann
Gute Nacht. Der Fachkräftemangel ist augenscheinlich noch viel
umfassender, als gedacht.
sid schrieb:> Wenn Ihr in RL auch so lahm seid, so ein simples Problen zu lösen,> dann> Gute Nacht. Der Fachkräftemangel ist augenscheinlich noch viel> umfassender, als gedacht.
Tzzz - es geht hier doch nicht einfach um eine Lösung...
Heiko L. schrieb:> Eben. So etwas weiß man i.d.R. in Bezug auf die konkrete Anwendung. Bei> der Definition eines allgemeinen intervall-checkers eher nicht.> Die meisten Zahlen sind klein.
Das war auch mein Gedanke, deshalb immer der Zusatz "im gültigen
Bereich". Bei einem "allgemeinen intervall-checker" muß man natürlich
alle die möglichen Fälle unterscheiden und ich bezweifle, dass man das
ausgerechnet in "C" und auf einem Controller machen würde! Aber
klar...jeder wie er mag.
Gruß Rainer
Dieter R. schrieb:> Nun sind wir endgültig bei Loriot gelandet?
Nicht unwahrscheinlich :-)...da der TO vermutlich tatsächlich einen
"Intervall-checker" im Hinterkopf hatte und die ganzen
Fallunterscheidungen sowohl gesehen hat, als auch vermeiden wollte, hat
er sich diese tolle Formel ausgedacht. Dass das auch hierbei nicht ohne
lästige Fallunterscheidungen abgeht, hat ihn nicht ruhen lassen!
"Es muß gehen, Andere machen es ja auch" (Loriot mit E.Harmann beim
Küssen)
Gruß Rainer
Dieter R. schrieb:> Trotzdem warte ich immer noch auf die Aufklärung, welche Lösung> (vorausgesetzt, sie funktioniert überhaupt im Sinne von mathematischer> Korrektheit) denn die effektivste ist.
Da der TO keinerlei Angaben über den Wertebereich von a gemacht hat,
kann es keine allgemeingültige Formel für dieses Problem geben. Punkt.
Grund:
Egal, was man für Zahlendarstellungen wählt (int, long, int64, float,
double), wird es immer einen gültigen Wert für a geben, der zusammen mit
b zu einem Überlauf oder einer NAN führt.
W.S.
Dieter R. schrieb:> Egon D. schrieb:>> Walter T. schrieb:>>>>> Was habe ich denn jetzt wieder falsch gemacht?>> Naja, für eine Aufgabe aus den ersten Stunden der> Anfängervorlesung ist der Thread ganz schön lang> geworden. Darauf wollte Walter T. wohl hinweisen.
Hmm. Mag sein, dass ich den Ursprungsbeitrag falsch
interpretiert habe.
Mir schien völlig offensichtlich, dass Walter nicht
einfach eine allgemeingültige Standardlösung für das
Standardproblem haben wollte, sondern eine Lösung für
einen speziellen Anwendungsfall gesucht hat, die auch
spezielle Eigenschaften haben sollte.
Sein Ansatz hat nämlich den Charme, dass er ohne
bedingte Sprünge formuliert werden kann, was auf
bestimmten Plattformen von Vorteil sein kann.
Das ist aber ein spezielles Optimierungsthema, was
meiner bescheidenen Meinung nach GAR NICHTS in einer
Anfängervorlesung zu suchen hat.
> Trotzdem warte ich immer noch auf die Aufklärung,> welche Lösung (vorausgesetzt, sie funktioniert> überhaupt im Sinne von mathematischer Korrektheit)> denn die effektivste ist.
Walters Lösung ist nach meinem Verständnis nicht korrekt
in dem Sinne, dass sie für beliebige Variablenbelegungen
korrekte Resultate liefert.
Das heißt aber nicht, dass sie nutzlos sein muss; in der
Bildbearbeitung sind die Inputdaten häufig auf [0,255]
beschränkt.
W.S. schrieb:> Egal, was man für Zahlendarstellungen wählt (int, long, int64, float,> double), wird es immer einen gültigen Wert für a geben, der zusammen mit> b zu einem Überlauf oder einer NAN führt.
Kannst du bitte ein Beispiel für einen solchen Fall angeben (wo ein
gültiger Wert für a zusammen mit b zu einem Überlauf oder einer NAN
führt)?
Danke.
Dieter R. schrieb:> Kannst du bitte ein Beispiel für einen solchen Fall> angeben (wo ein gültiger Wert für a zusammen mit b> zu einem Überlauf oder einer NAN führt)?
W.S. schrieb:> Egal, was man für Zahlendarstellungen wählt (int, long, int64, float,> double), wird es immer einen gültigen Wert für a geben, der zusammen mit> b zu einem Überlauf oder einer NAN führt.
Nur wenn man a und b explizit addiert. Johann hat oben ja gezeigt, dass
es auch anders geht.
Ganz unabhängig davon stellt sich für mich die Frage, ob man in der
realen Welt überhaupt so eine Funktion wie von Walter gewünscht
schreiben würde. Mir erscheint es sinnvoller, erst einmal eine Funktion
1
boolisInRange(intlower,intupper,intval){
2
returnval>=lower&&val<=upper;
3
}
zu schreiben, die ganz banal, aber auf effiziente Art und Weise die
allermeisten Fälle abdeckt.
Wenn dann lower oder upper tatsächlich als Summe von zwei Werten gegeben
ist, ist es sinnvoller, diese Summe auf Aufruferseite zu berechnen, da
dort mehr Informationen darüber vorliegen, in welchen Bereichen diese
Werte tatsächlich liegen, so dass in den allermeisten Fällen ein
Überlauf auch ohne spezielle Behandlung ausgeschlossen werden kann.
Ebenso dürfte dort in den meisten Fällen aus dem Kontext hervorgehen,
welche der beiden Intervallgrenzen die größere ist, so dass man auch auf
die diesbezügliche Fallunterscheidung verzichten kann.
Sollten die Grenzen dennoch einmal unsortiert vorliegen, ist es
sinnvoll, den Vergleich und ggf. die Vertauschung ebenfalls auf
Aufruferseite zu machen, denn die Chancen stehen hoch, dass man die
sortierten Grenzen noch an anderer Stelle benötigt.
Versucht man hingegen wie Walter, auch alle seltenen Fälle mit einer
Eierlegendewollmilchsaufunktion zu erschlagen, wird der Code nur
fehlerträchtiger und ineffizienter, ohne dass dabei an anderer Stelle,
bspw. bei der Übersichtlichkeit, etwas hinzugewonnen wird.
Um die richtige Vorgehensweise endgültig entscheiden zu können, müsste
man natürlich mehr über den konkreten Anwendungsfall wissen.
Egon D. schrieb:> Sein Ansatz hat nämlich den Charme, dass er ohne> bedingte Sprünge formuliert werden kann, was auf> bestimmten Plattformen von Vorteil sein kann.
Das wäre mein Ansatz auch, wenn man | statt || schreibt:
A. S. schrieb:> return c==a|c==a+b|c<a^c<a+b;
Zudem ohne Überlauf bei korrektem Intervall (im Gegensatz zum UP und den
meisten, die andere Addition/Subtraktion/Multiplikation als a+b haben (
c-a kann auch bei korrekten Zahlen fehlerhaft überlaufen, a+b ist per
Definition Teil der Aufgabe)
Wenn man a+b trotzdem abfangen will, ist der Rest sinnlos, da die
wichtigen Abfragen dafür auch nötig sind. Beispiel mit ab=a+b:
Die Fälle mit INT_MIN und INT_MAX sind aber trivial, so dass die
komplette Auswertung kleiner ist.
1
if(b<0){if(c<=a){return(INT_MIN-b>a)?1:c>=a+b;}}
2
else{if(c>=a){return(INT_MAX-b<a)?1:c<=a+b;}}
3
return0;
Ich schicke also diese beiden Versionen ins Rennen. Die obere sprunglos
kurz entsprechend der Aufgabe, diese als minimal mit a+b-Check.
Yalu X. schrieb:> Ebenso dürfte dort in den meisten Fällen aus dem Kontext hervorgehen,> welche der beiden Intervallgrenzen die größere ist, so dass man auch auf> die diesbezügliche Fallunterscheidung verzichten kann.
Bei uns nicht. Ein inrange(val, limit1, limit2) ist eine der
meistgenutzten Funktionen.
Egon D. schrieb:> in der> Bildbearbeitung sind die Inputdaten häufig auf [0,255]> beschränkt.
...und wenn man z.B. einen 12Bit AD-Wandler einließt, dann weiß man
auch, in welchem Wertebereich man sich aufhält und kann gegebenfalls das
Produkt mit dem nächstgrößeren Zahlentyp "säubern". Trotzdem würde ich
in allen Fällen dem größer/kleiner Vergleich den Vorzug geben. Das sind
gerade mal zwei Subtraktionen zum gewünschten Ergebnis. Insofern ist die
Frage des TO trotz oder gerade deswegen akademisch.
Gute Nacht, Rainer
Egon D. schrieb:> Sein Ansatz hat nämlich den Charme, dass er ohne> bedingte Sprünge formuliert werden kann, was auf> bestimmten Plattformen von Vorteil sein kann.
Das wäre mein Ansatz auch, wenn man | statt || schreibt:
A. S. schrieb:> return c==a|c==a+b|c<a^c<a+b;
Zudem ohne Überlauf bei korrektem Intervall (im Gegensatz zum UP und den
meisten, die andere Addition/Subtraktion/Multiplikation als a+b haben (
c-a kann auch bei korrekten Zahlen fehlerhaft überlaufen, a+b ist per
Definition Teil der Aufgabe)
Wenn man a+b trotzdem abfangen will, ist der Rest sinnlos, da die
wichtigen Abfragen dafür auch nötig sind. Beispiel mit ab=a+b:
Die Fälle mit INT_MIN und INT_MAX sind aber trivial, so dass die
komplette Auswertung kleiner ist.
1
if(b<0){if(c<=a){return(INT_MIN-b>a)?1:c>=a+b;}}
2
else{if(c>=a){return(INT_MAX-b<a)?1:c<=a+b;}}
3
return0;
Ich schicke also diese beiden Versionen ins Rennen. Die obere sprunglos
kurz entsprechend der Aufgabe, diese als minimal mit a+b-Check.
P.S.: ein || oder && betrachte ich als Sprung-Äquivalent, da Code
"übersprungen" wird, bei | nicht.
Yalu X. schrieb:> Ebenso dürfte dort in den meisten Fällen aus dem Kontext hervorgehen,> welche der beiden Intervallgrenzen die größere ist, so dass man auch auf> die diesbezügliche Fallunterscheidung verzichten kann.
Bei uns nicht. Ein inrange(val, limit1, limit2) ist eine der
meistgenutzten Funktionen.
A. S. schrieb:> Ich schicke also diese beiden Versionen ins Rennen. Die obere sprunglos> kurz entsprechend der Aufgabe, diese als minimal mit a+b-Check.
Für die Over-/Underflow Prüfung (signed addition = UB) brauche ich einen
Sprung (||). Dazu kommt ein zweiter für den Vorzeichentest für b.
1
boolinRange(inta,intb,intc)
2
{
3
if(b>=0){return(a<=c)&((a>INT_MAX-b)||(c<=a+b));}
4
else{return((a<INT_MIN-b)||(a+b<=c))&(c<=a);}
5
}
Welches ist deine Version, die das ohne Sprünge löst?
Btw: Falls einer der Beiträge von Wilhelm (mit dem casten nach unsigned)
das eleganter löst, dann habe ich es irgendwie verpasst. Welches war die
Version, die beliebige a,b,c korrekt auswertet?
Yalu X. schrieb:> Wenn dann lower oder upper tatsächlich als Summe> von zwei Werten gegeben ist, [...]
Das ist in der Tat sehr ungewöhnlich, ja.
> Ebenso dürfte dort in den meisten Fällen aus dem> Kontext hervorgehen, welche der beiden> Intervallgrenzen die größere ist,
Das nicht unbedingt.
Stelle Dir einfach eine Bildverarbeitungsfunktion
vor, die darauf testen möchte, ob der Grauwert des
Zentralpixels zwischen den Grauwerten seiner beiden
Nachbarn liegt.
> Versucht man hingegen wie Walter, auch alle seltenen> Fälle mit einer Eierlegendewollmilchsaufunktion zu> erschlagen, wird der Code nur fehlerträchtiger und> ineffizienter,
"Fehlerträchtiger" -- ja.
"ineffizienter" -- nein, nicht unbedingt.
> ohne dass dabei an anderer Stelle, bspw. bei der> Übersichtlichkeit, etwas hinzugewonnen wird.
Beispiel aus der Praxis: Graustufenbild (7800 x 5100)
um 90° drehen.
Generisches Tool: ca. 7 Sekunden Laufzeit.
Handoptimiertes Programm: ca. 1.5 Sekunden Laufzeit.
Bei den ca. 300 Dateien, die in einem Schwung anfallen,
gibt jede Sekunde Programmlaufzeit mehr einen Verlust
von 5 Minuten (!).
> Um die richtige Vorgehensweise endgültig entscheiden> zu können, müsste man natürlich mehr über den> konkreten Anwendungsfall wissen.
Eben.
Darüber, dass man solche Stunts nur im äußersten Notfall
bringen sollte, brauchen wir nicht zu diskutieren. Aber
das schon die Idee abstrus ist, das stimmt einfach nicht.
A. S. schrieb:> Egon D. schrieb:>> Sein Ansatz hat nämlich den Charme, dass er ohne>> bedingte Sprünge formuliert werden kann, was auf>> bestimmten Plattformen von Vorteil sein kann.>> Das wäre mein Ansatz auch, wenn man | statt || schreibt:> [...]
Gut möglich. Meine C-Kenntnisse sind zu schlecht, um
das beurteilen zu können.
Meine Aussage war nur die, dass Walters Lösungsidee
angenehme Eigenschaften hat, die die Standardlösung
nicht aufweist.
Dass es NOCH bessere Ansätze geben kann (und wird),
wird dadurch nicht bestritten.
Egon D. schrieb:> Handoptimiertes Programm: ca. 1.5 Sekunden Laufzeit.
Ist da immer noch das Rotieren der Flaschenhals? Das lässt sich
wunderbar mit einer GPU beschleunigen, kostet darauf ja quasi nichts.
Heiko L. schrieb:> sid schrieb:> Wenn Ihr in RL auch so lahm seid, so ein simples Problen zu lösen, dann> Gute Nacht. Der Fachkräftemangel ist augenscheinlich noch viel> umfassender, als gedacht.>> Tzzz - es geht hier doch nicht einfach um eine Lösung...
Nein, natürlich nicht. Es geht ausschließlich um P€nisfechten. Ändert
aber nichts. In RL genau so unbrauchbar.
Mikro 7. schrieb:> if (b >= 0) { return (a <= c) & ((a > INT_MAX-b) || (c <= a+b)) ; }> else { return ((a < INT_MIN-b) || (a+b <= c)) & (c <= a) ; }
Das ist doch meine Lösung, nur jetzt ein Sprung umformuliert, also 3
statt 5.
> Welches ist deine Version, die das ohne Sprünge löst?> return c==a|c==a+b|c<a^c<a+b;
Ich kenne keine sichere Version für a+b ohne Sprünge, abgesehen von
unsigned casts. Ich halte genau diese Addition aber für erlaubt, da Teil
der Aufgabe, eine Rechnung z.b. a-c hingegen nicht
Walter T. schrieb:> Ich will wissen, ob sich die Variable c im Intervall zwischen a und a+b> (inklusive) befindet.
Wenn a+b überlaufen kann, bräuchten wir auch eine Definition, was dann
zurückgegeben werden soll.
Btw.: Gibt es jemanden, der wirklich jede signed Operation auf Überlauf
testet oder testen lässt?
Zeno schrieb:> Wilhelm M. schrieb:>> So langsam beginnst Du zu verstehen. Schau nochmal ganz genau hin.>> Meinst Du! Nö da muß ich nicht noch mal hinschauen.>> Ist halt doof wenn die Werkzeuge, in dem Fall der Compiler, nicht so> funktionieren wie man es erwartet bzw. von anderen gewohnt ist.
Ist es wirklich soooo schwer zu ergründen, was die Metafunktion
1
enclosing_t<>
wohl tun könnte? Gerade wenn Du ja schon verstanden hast, dass es an
der Integralpromotion hängt, ob das Ergebnis der
Intervallgrenzenberechnung korrekt ist. BTW: das geht mit jedem
C++-Compiler ab c++98. Und das wird auch schon dieser Zeit eingesetzt.
Hat also gar nichts mit neu zu tun und ist eine normale C++-Technik.
Yalu X. schrieb:> W.S. schrieb:>> Egal, was man für Zahlendarstellungen wählt (int, long, int64, float,>> double), wird es immer einen gültigen Wert für a geben, der zusammen mit>> b zu einem Überlauf oder einer NAN führt.>> Nur wenn man a und b explizit addiert. Johann hat oben ja gezeigt, dass> es auch anders geht.
Oder man führt bei Argumenten des DT int16_t die Rechnung in int32_t
aus, oder bei Argumenten int8_t ind int16_t aus, ff. Siehe mein
Quellcode von oben.
> Wenn dann lower oder upper tatsächlich als Summe von zwei Werten gegeben> ist, ist es sinnvoller, diese Summe auf Aufruferseite zu berechnen,
Genau das war mein Ansatz im ersten Beispiel, das ich gepostet hatte!
> Versucht man hingegen wie Walter, auch alle seltenen Fälle mit einer> Eierlegendewollmilchsaufunktion zu erschlagen, wird der Code nur> fehlerträchtiger und ineffizienter, ohne dass dabei an anderer Stelle,> bspw. bei der Übersichtlichkeit, etwas hinzugewonnen wird.>> Um die richtige Vorgehensweise endgültig entscheiden zu können, müsste> man natürlich mehr über den konkreten Anwendungsfall wissen.
In C++ kann man das ganz elegant wie in meinen Beispielen lösen.
Ja, jetzt kommt wieder der Einwand, dass die Metafunktion
`enclosing_t<>` nicht von mir angegeben wurde. Na ja, die hat ungefähr
die Komplexität des unsäglichen Präprozessormakros `max()`, was hier
eingeführt wurde, und als unscoped-Makro ganz schnell irgendwelche
Quelltexte zerreisst. Wer sich ein klein bisschen Gedanken macht und
etwas C++98 kann, der kann auch diese Metafunktion schnell hinschreiben.
Die Frage ist nicht theoretisch. Der Anwendungsfall ist sehr konkret.
Ich will prüfen, ob eine Variable a im letzten Update-Schritt an
Variable c vorbeigelaufen ist - oder meinetwegen drübergesprungen. Die
Summe kommt daher, daß die Update-Breite noch greifbar ist, so dass ich
mir das Speichern des alten Werts sparen kann.
"In echt" sieht die Funktion (noch) so aus:
1
/** Pruefen, ob x im Intervall [u....u + deltaU] liegt
2
*
3
* @param[in] x Vergleichswert
4
* @param[in] u obere oder untere Intervallgrenze
5
* @param[in] deltaU (positive oder negative) Intervallbreite
6
* @return u <= x <= u+deltaU || u+deltaU <= x <= u */
7
boolhasCrossed(int64_tx,int64_tu,int32_tdeltaU)
8
{
9
int32_tdiff=x-u;
10
return(diff*(diff+deltaU))<=0;
11
}
Allerdings war die Frage ja wirklich allgemein gedacht: Wie löst das ein
erfahrenerer Programmierer, der sich nicht auf dass Wissen, dass zwei
Variablen dicht beieinander liegen und dass das Delta klein ist,
verlassen will. Deswegen habe ich im Eröffnungspost eine einfachere
Variante gepostet.
Jetzt weiß ich: Der erfahrenere Programmierer, der das allgemein lösen
will, hat im Vergleich zur Einfachheit der Problemstellung ganz schön
viel zu tun (Johann L.). Wenn ich das richtig gesehen habe, ist das
immer noch die einzige Lösung, die Überlauf komplett vermeidet.
Oder es müssen eben Einschränkungen im Wertebereich gemacht werden, dann
läßt läßt sich das mit einem kleinen Stapel Vergleichsoperationen sehr
direkt lösen.
Und ich weiß jetzt auch wieder zu schätzen, daß ich im Alltag wieder
viel weniger mit Softwerkern zu tun habe, als das noch vor ein paar
Jahren der Fall war.
Walter T. schrieb:> Jetzt weiß ich: Der erfahrenere Programmierer, der das allgemein lösen> will, hat im Vergleich zur Einfachheit der Problemstellung ganz schön> viel zu tun (Johann L.). Wenn ich das richtig gesehen habe, ist das> immer noch die einzige Lösung, die Überlauf komplett vermeidet.
Nein. Der C++-Code aus meinen Beiträgen ist auch korrekt, aber eben
natürlich C++ und - oh Graus - auch noch mit templates ;-) (Übrigens
compiliert er nicht, wenn er nicht korrekt sein kann).
> Oder es müssen eben Einschränkungen im Wertebereich gemacht werden, dann> läßt läßt sich das mit einem kleinen Stapel Vergleichsoperationen sehr> direkt lösen.
Vielleicht bekommt C ja auch irgendwann mal `Contracts` ...
Fuck! Warum ist bei Softwerkern die Quote an Menschen so hoch, die
grundsätzlich nie die Frage behandeln, wie sie gestellt wurde, sondern
lieber eine eigene Version beantworten, die ihre
Lieblings-Programmierumgebung beinhaltet? Wie muß man gestrickt sein, um
mit euch klarzukommen?
Warum nicht direkt darauf hinweisen, dass das in Pearl oder Python
komplett besser sei?
Walter T. schrieb:> Fuck! Warum ist bei Softwerkern die Quote an Menschen so hoch, die> grundsätzlich nie die Frage behandeln, wie sie gestellt wurde, sondern> lieber eine eigene Version beantworten, die ihre> Lieblings-Programmierumgebung beinhaltet? Wie muß man gestrickt sein, um> mit euch klarzukommen?
Weil es genau Dein Problem lösen würde.
Und weil manchmal die Frage nach dem Hammer im Sinne des Fragenden auch
mal allgemeiner beantwortet werden darf - vorausgesetzt, der Fragende
möchte über den Tellerrand hinausschauen.
Was ist jetzt gleich die Antwort auf die Frage des TO? Wissen wir, was
eine effektive (Definition je nach Gusto) Lösung des mathematischen
und/oder programmiertechnischen Problems ist, mit und ohne Test auf
Bereichsüberschreitung von was auch immer?
Bin ich der einzige, der komplett den Faden verloren hat und viele mehr
oder (meist) weniger tiefschürfende Betrachtungen sieht, aber keine
Lösung im Sinne einer Aufgabe im Begleitseminar zur (Anfänger-)
Vorlesung?
Wilhelm M. schrieb:> Weil es genau Dein Problem lösen würde.
Das ist doch auch wieder eine Null-Aussage. Deine Einlassungen in diesem
Thread erinnern erstaunlich stark an Menschen, die beim
Mensch-Ärgere-Dich-Nicht-Spiel während des Spiels neue Regeln erfinden,
weil sie nicht verlieren können. Beim Schach, wo das nicht geht, wird
dann stattdessen im Endspiel bei jedem Zug stundenlang nachgedacht,
selbst wenn nur noch eine Figur zum Ziehen da ist. So kann man den
Spielspaß für jeden anderen zuverlässig minimieren.
Zumindest als man sie noch eingeladen hat. Irgendwann weiß man ja, wenn
man nicht einladen darf, wenn man verhindern will, daß eine solche
Person das mitbekommt und auch kommt.
Walter T. schrieb:> (Johann L.). Wenn ich das richtig gesehen habe, ist das immer noch die> einzige Lösung, die Überlauf komplett vermeidet.
Überlauf vermeidet meine Version auch. Von Johann sehe ich aber keine,
die für negative b richtig rechnet. Egal was genau du bei Überlauf
erwartest.
Walter T. schrieb:> Wilhelm M. schrieb:>> Weil es genau Dein Problem lösen würde.>> Das ist doch auch wieder eine Null-Aussage.
Ich finde die Aussage sehr klar: es löst Dein Problem, und zwar
allgemein und korrekt, und ohne, dass Du Dich groß umstellen musst.
> Deine Einlassungen in diesem> Thread erinnern erstaunlich stark an Menschen,
... bla bla ...
Mein obiger Code liefert für eine Intervallprüfung von in16_t folgendes
(x86_64):
1
main:
2
moveax,32762
3
movWORDPTR[rsp-2],ax
4
movzxeax,WORDPTR[rsp-2]
5
cmpax,32756
6
setgal
7
movzxeax,al
8
ret
Deiner liefert:
1
isdrin(int,int,int):
2
addesi,edi
3
subedi,edx
4
subesi,edx
5
imulesi,edi
6
testesi,esi
7
setleal
8
ret
9
main:
10
moveax,32762
11
movecx,32777
12
movWORDPTR[rsp-2],ax
13
movsxedx,WORDPTR[rsp-2]
14
moveax,32757
15
subeax,edx
16
subecx,edx
17
imuleax,ecx
18
testeax,eax
19
setleal
20
movzxeax,al
21
ret
Bei int32_t ist das Ergebnis, der Unterschied natürlich noch krasser
(vorausgesetzt, man ändert Deine ursprüngliche Funktion dazu).
Dieter R. schrieb:> Kannst du bitte ein Beispiel für einen solchen Fall angeben (wo ein> gültiger Wert für a zusammen mit b zu einem Überlauf oder einer NAN> führt)?
hat doch Wilhem für int schon hier
Beitrag "Re: C: Prüfen, ob Variable in Intervall" gemacht.
a und b sind gültige Integerwerte aber die Summe überschreitet den
maximalen Wertebereich von int.
Yalu X. schrieb:> Sollten die Grenzen dennoch einmal unsortiert vorliegen, ist es> sinnvoll, den Vergleich und ggf. die Vertauschung ebenfalls auf> Aufruferseite zu machen, denn die Chancen stehen hoch, dass man die> sortierten Grenzen noch an anderer Stelle benötigt.
Du hast in allen recht was Du in Deinem Post schreibst, aber an dieser
Stelle muß ich einfach noch mal auf meiner Lösung rumreiten.
Seien a und b die vorgegebenen Grenzwerte, die ihrerseits gültige
Integerzahlen darstellen, dann ist es doch ein Leichtes per Bestimmung
von Maximum und Minimum Lower und Upper festzulegen.
Entweder über das von mir vorgeschlagene Präprozessormakro oder über
if-else.
Also so hier:
1
/* min/max Präprozessormakro */
2
lower=min(a,b);
3
upper=max(a,b);
oder eben so
1
if(a<=b)
2
{
3
lower=a;
4
upper=b;
5
}else{
6
lower=b;
7
upper=a;
8
}
oder von mir aus auch so
1
lower=(a<=b)?a:b;
2
upper=(a>b?a:b;
Ist alles gültiger Code der unter der oben genannten Bedingung
funktioniert.
Das ist doch wenig Aufwand und ich bin anschließend sicher das die
Grenzen lower und upper richtig gesetzt sind.
Zeno schrieb:> Yalu X. schrieb:>> Sollten die Grenzen dennoch einmal unsortiert vorliegen, ist es>> sinnvoll, den Vergleich und ggf. die Vertauschung ebenfalls auf>> Aufruferseite zu machen, denn die Chancen stehen hoch, dass man die>> sortierten Grenzen noch an anderer Stelle benötigt.
Lustig, so langsam nähert man sich einer OO-Sichtweise ;-) Na na na ...
Wilhelm M. schrieb:> Ist es wirklich soooo schwer zu ergründen, was die Metafunktion> enclosing_t<>
Zum einen programmiere ich in nicht oder nur sehr sehr selten in C++ ,
weshalb die Spezifika von C++ eben nicht kenne.
Zum anderen warum soll ich jetzt Deinen Code bis ins letzte Detail
auseinandernehmen um zu prüfen das er funktioniert. Du hast einen Code
gepostet der das vom TO dargestellte Problem lösen soll, demzufolge mußt
Du beweisen das Dein Code für's erste funktioniert, notfalls durch
entsprechende Kommentierung damit er auch von nich C++ Profis
nachvollzogen werden kann.
Ansonten habe ich keine Lust mehr mit Dir darüber zu diskutieren, da wir
eh nie ein Ende finden werden.
W.S. und auch Yalu haben es gesagt: Das Entscheidende ist das die
Bereichsgrenzen im gültigen Integerbereich bleiben. Unter dieser
Voraussetzung funktioniert mein Code und das reicht mir für's erste.
Wenn ich mehr brauchen sollte, werde ich auch eine Lösung finden. Das
wird aber definitiv nicht die Deinige sein, allein schon deswegen weilen
Code benutzt der nicht allgemein zugänglich ist und Dich hartnäckig
weigerst diesen zu posten. Das Zweite ist, selbst wenn ich mir jetzt die
Mühe mache Deinen Code bis ins letzte Detail zu verstehen, dann stehe
ich in 2 Jahren wieder vor dem gleichen Problem. Da schreibe ich mir
lieber was Eigenes, was dann auch unter C oder mit sprachspezifischen
Anpassungen auch unter Pascal, JS und evtl. PHP funktioniert.
Wilhelm M. schrieb:> Lustig, so langsam nähert man sich einer OO-Sichtweise ;-) Na na na ...
Jetzt muß ich doch noch mal was sagen.
Mit OO hat das reinweg gar nichts zu tun. Schau doch einfach mal über
Deinen C++ Tellerrand, es gibt halt auch noch anderes neben OO.
Wilhelm M. schrieb:> Ich finde die Aussage sehr klar: es löst Dein Problem, und zwar> allgemein und korrekt, und ohne, dass Du Dich groß umstellen musst.
Bis auf die unwesentliche Kleinigkeit, daß Walter T. (zumindest dem
Anschein und auch dem Threadtitel nach) in C programmiert, bzw. eine
Lösung in C sucht.
Oliver
Zeno schrieb:> Wilhelm M. schrieb:>> Ist es wirklich soooo schwer zu ergründen, was die Metafunktion>> enclosing_t<>>> Zum einen programmiere ich in nicht oder nur sehr sehr selten in C++ ,> weshalb die Spezifika von C++ eben nicht kenne.
Das merkt man, und deswegen solltest Du Dich dann mal mit Bemerkungen
dazu zurückhalten.
> Zum anderen warum soll ich jetzt Deinen Code bis ins letzte Detail> auseinandernehmen um zu prüfen das er funktioniert.
Das einzige wichtige für die zweite Variante, was nicht im Post
enthalten ist, ist enclosing_t<>. Und da Du offensichtlich noch nicht
einmal eine Idee aus dem Namen oder der Verwendung im Code hast bzw. Dir
vorstellen kannst, was das machen könnte, ist das auch für mich
vollkommen witzlos, dass noch nachzureichen. Dazu hast Du wirklich zu
wenig Ahnung von C++.
> W.S. und auch Yalu haben es gesagt: Das Entscheidende ist das die> Bereichsgrenzen im gültigen Integerbereich bleiben.
Liest meinen ersten Post hier nochmal, da steht es schon drin.
> Wenn ich mehr brauchen sollte, werde ich auch eine Lösung finden. Das> wird aber definitiv nicht die Deinige sein, allein schon deswegen weilen> Code benutzt der nicht allgemein zugänglich ist und Dich hartnäckig> weigerst diesen zu posten.
Falls jemand sich tatsächlich mal damit auseinandersetzt, werde ich das
tun (obwohl eigentlich unnötig). Doch solange sich die Kritik nur auf
oberflächliche Aussagen beschränkt, sehe ich da keinen Handlungsbedarf.
> Das Zweite ist, selbst wenn ich mir jetzt die> Mühe mache Deinen Code bis ins letzte Detail zu verstehen, dann stehe> ich in 2 Jahren wieder vor dem gleichen Problem.
Dann hast Du also auch in den nächsten zwei Jahren nichts gelernt ...
schade - für Dich.
Oliver S. schrieb:> Wilhelm M. schrieb:>> Ich finde die Aussage sehr klar: es löst Dein Problem, und zwar>> allgemein und korrekt, und ohne, dass Du Dich groß umstellen musst.>> Bis auf die unwesentliche Kleinigkeit, daß Walter T. (zumindest dem> Anschein und auch dem Threadtitel nach) in C programmiert, bzw. eine> Lösung in C sucht.
Das stimmt!
Aber, und nur deswegen habe ich das hier gepostet: C und C++ sind ohne
Schwierigkeiten leicht interoperabel. Und wenn dieser use-case zunächst
der Einzige ist, den er in C++ umsetzt und alles andere in C belässt, so
hat er doch damit schon seine C-Blase verlassen und eine Lösung, die für
alle Typen korrekt ist und für Typen, für die sie nicht korrekt sein
kann, nicht kompiliert.
Zeno schrieb:> Dieter R. schrieb:>> Kannst du bitte ein Beispiel für einen solchen Fall angeben (wo ein>> gültiger Wert für a zusammen mit b zu einem Überlauf oder einer NAN>> führt)?>> hat doch Wilhem für int schon hier> Beitrag "Re: C: Prüfen, ob Variable in Intervall" gemacht.>> a und b sind gültige Integerwerte aber die Summe überschreitet den> maximalen Wertebereich von int.
Und Yalu hat (wohl nicht zum ersten Mal) darauf hingewiesen, warum das
irrelevant ist:
Autor: Yalu X. (yalu) (Moderator)
Datum: 22.07.2019 23:47
>W.S. schrieb:>> Egal, was man für Zahlendarstellungen wählt (int, long, int64, float,>> double), wird es immer einen gültigen Wert für a geben, der zusammen mit>> b zu einem Überlauf oder einer NAN führt.> Nur wenn man a und b explizit addiert. Johann hat oben ja gezeigt, dass> es auch anders geht.
Die Diskussion dreht sich ergebnislos im Kreis. Mit zunehmender
Geschwindigkeit.
Wilhelm M. schrieb:> Welcher Teil von template<typename T>> [...]> ist für Dich kryptisch?
Für einen sterblichen C-Programmierer ist das template-Gedöns absolut
kryptisch.
Selbst wenn man willig ist und es verstehen und ausprobieren will, hilft
es hier nicht weiter, wenn Du nicht vollständig kompilierbare Beispiele
bringst.
Hinzu kommt, dass nicht unbedingt jeder den allerneuesten C++-Compiler
auf seinem PC hat, denn Deine Beispiele setzen meist mindestens C++20
voraus, obwohl C++20 offiziell erst seit letztem Wochenende
verabschiedet ist. Deine offensichtliche Absicht, Deinen Lesern den
Horizont zu erweitern, ist so leider von vornherein zum Scheitern
verurteilt.
Schade.
Walter T. schrieb:> Jetzt weiß ich: Der erfahrenere Programmierer, der das allgemein lösen> will, hat im Vergleich zur Einfachheit der Problemstellung ganz schön> viel zu tun (Johann L.). Wenn ich das richtig gesehen habe, ist das> immer noch die einzige Lösung, die Überlauf komplett vermeidet.
Der Ansatz von Wilhelm, die Berechnung im nächstgrößeren Integertyp
durchzuführen, vermeidet das Problem ebenfalls, zumindest für den
Datentyp, den du in deinem Eröffnungsbeitrag, verwendest, nämlich int.
Wenn vor der Addition von a und b beide Operanden auf int64_t erweitert
werden, ist ein Überlauf ausgeschlossen.
In deiner "echten" Funktion
1
boolhasCrossed(int64_tx,int64_tu,int32_tdeltaU)
verwendest du allerdings int64_t als Argumenttyp, so dass für Wilhelms
Ansatz 128-Bit-Integers erforderlich wären, die es aber für den ARM
(zumindest beim GCC) nicht gibt. Johanns Lösung hingegen würde auch bei
64-Bit-Argumenten den Überlauf vermeiden, während Wilhelms Lösung dann
vermutlich zu einer Fehlermeldung des Compilers führen würde (je nachdem
wie er etl::enclosing_t implementiert hat).
Dieter R. schrieb:> W.S. schrieb:>>> Egal, was man für Zahlendarstellungen wählt (int, long, int64, float,>> double), wird es immer einen gültigen Wert für a geben, der zusammen mit>> b zu einem Überlauf oder einer NAN führt.
Nope. Ich habe oben eine Lösung für alle möglichen Eingabewerte
gezeigt, die auch keinen breiteren Datentyp braucht.
Beispiel Fall b >= 0 und c >= a: Es soll getestet werden, ob c in [a,
a+b] gilt.
Bei den beiden Vergleichen gibt es keinen Überlauf, und wir gehen davon
aus, dass alle Werte signed N-Bit Werte sind. Sei u(x) die
Unsigned-Version von x, d.h. es wird solange 2^N auf x addiert bis das
Ergebnis im halboffenen Intervall [0, 2^N) liegt.
Alle Werte werden zu Unsigned gecastet. Der ausgeführte Vergleich ist
daher:
1
u(u(c) - u(a)) <= u(b)
Auf der rechten Seite gilt wegen b >= 0 dass u(b) = b. Der Vergleich
geht also einfach gegen b.
Auf der linken Seite entsprechen die 2 inneren u() der Cast-Semantik von
C/C++, und das äußere u() ist die Semantik der Unsigned-Subtraktion mit
Wrap-Around bzw. mod 2^N im kleinsten, nicht-negativen Restsystem.
Für die linke Seite gilt wegen c < 2^{N-1} und -a <= 2^{N-1} dass c - a
< 2^N, wir haben also 0 <= c - a < 2^N und damit heben sich die
Additionen von 2^N, welche die u's bewirken, insgesamt gegenseitig auf.
Daher:
1
u(u(c) - u(a)) <= u(b) <=> c - a <= b <=> c <= a + b
Einen ähnlichen Beweis führt man für den 2. Fall mit b < 0 und c <= a um
die Bedingung für c in [a+b, a] zu erhalten.
Das mag alles kompliziert erscheinen, aber ich finde es immer noch
besser nachvollziehbar als die Template-Lösung. Alles was gebraucht
wird ist die Semantik von Cast, Subtraktion und Vergleich.
Zudem ist die Lösung für alle Eingaben korrekt, was bei den Vorschlägen,
die "a+b" enthalten, nicht der Fall ist. Die sagen einfach nur "Seien
die Werte so, dass alles gut geht".
Und wer jetzt auf C/C++ flucht: Wenn man diese Lösung in Assembler
implementiert dann ist der Beweis, dass das Verfahren korrekt ist, der
gleiche. Wenn die interne Darstellung von Signed das 2-er Komplement
ist, dann ist der Beweis die Rechtfertigung, warum in Assembler der
Vergleich c - a <= b als Unsigend ausgeführt wird und nicht als
Signed, und warum der ggf. auftretende Underflow + Wrap bei der
Subtraktion kein Probem ist. Und falls die Darstellung eine andere
ist, z.B. 1-Komplement, dann kommen für die Unsigned-Umwandlung u() eben
Instruktionen hinzu, ansonsten bleibt aber alles gleich.
Frank M. schrieb:> Wilhelm M. schrieb:>> Welcher Teil von template<typename T>>> [...]>> ist für Dich kryptisch?
Warum bekommt man auf so eine Nachfrage eigentlich keine normale
Antwort? Wer dann natürlich mit "alles" antworten muss, der kann
natürlich nicht mitreden.
> Für einen sterblichen C-Programmierer ist das template-Gedöns absolut> kryptisch.
Das liegt m.E. nur daran, dass die meisten das Thema statische
Polymorphie komplett falsch anfangen ... Wer aber verstanden hat, dass
das Typ-System einer statisch getypten Sprache DAS Mittel ist, um
effizienten, korrekten und generischen Code zu schreiben, für den ist
das eine wahre Goldgrube.
> Selbst wenn man willig ist und es verstehen und ausprobieren will, hilft> es hier nicht weiter, wenn Du nicht vollständig kompilierbare Beispiele> bringst.
Nochmal: das Einzige, was oben im zweiten Beispiel fehlte, war die eine
Funktion enclosing_t<>:
https://godbolt.org/z/hwXO_j
Man muss nur in Zeile 74 den Typ ändern. Die Werte passen sich dann ja
an.
> Hinzu kommt, dass nicht unbedingt jeder den allerneuesten C++-Compiler> auf seinem PC hat. Deine offensichtliche Absicht, Deinen Lesern den> Horizont zu erweitern, ist daher von vornherein zum Scheitern> verurteilt.
Und nochmal wie oben schon geschrieben: lässt man die
Requirements/Concepts weg, ist es aktueller C++-Standard.
Yalu X. schrieb:> Johanns Lösung hingegen würde auch bei> 64-Bit-Argumenten den Überlauf vermeiden, während Wilhelms Lösung dann> vermutlich zu einer Fehlermeldung des Compilers führen würde (je nachdem> wie er etl::enclosing_t implementiert hat).
Bingo! Entweder es ist korrekt oder es kompiliert nicht. So soll es ja
sein.
Johann L. schrieb:> Dieter R. schrieb:>> W.S. schrieb:>>>>> Egal, was man für Zahlendarstellungen wählt (int, long, int64, float,>>> double), wird es immer einen gültigen Wert für a geben, der zusammen mit>>> b zu einem Überlauf oder einer NAN führt.>> Nope. Ich habe oben eine Lösung für alle möglichen Eingabewerte> gezeigt, die auch keinen breiteren Datentyp braucht.>> Beispiel Fall b >= 0 und c >= a: Es soll getestet werden, ob c in [a,> a+b] gilt.
[...]
Das Dumme ist nur, dass dieser Beweis unnötig ist, da es zu demselben
Maschinencode führt wie meine Variante (mit Ausnahme 64Bit). Wobei die
template-Variante natürlich den an die Typen korrekt angepassten Code
liefert (kleiner).
Wilhelm M. schrieb:> Bingo! Entweder es ist korrekt oder es kompiliert nicht. So soll es ja> sein.
Was dich aber trotzdem an der Stelle, an der du ein inRange mit 64 Bit
Integer als Parameter brauchst, mit einem ungelösten Problem
zurücklässt.
Oliver
Oliver S. schrieb:> Wilhelm M. schrieb:>> Bingo! Entweder es ist korrekt oder es kompiliert nicht. So soll es ja>> sein.>> Was dich aber trotzdem an der Stelle, an der du ein inRange mit 64 Bit> Integer als Parameter brauchst, mit einem ungelösten Problem> zurücklässt.
Stimmt. Wichtig aber: es kompiliert nicht, und produziert zur Laufzeit
kein falsches Ergebnis.
Aber Leute: es ist doch jetzt einfach aus Johanns Ansatz und dem
Template eine gemeinsame Version zu schaffen, oder? Dank an Johann.
Johann L. schrieb:> ch habe oben eine Lösung für alle möglichen Eingabewerte gezeigt, die> auch keinen breiteren Datentyp braucht.
Dann zitiere doch bitte die Version, die funktioniert. Ich finde keine
von Dir. Mag natürlich sein, das andere oder meine auch Fehler
enthalten, ist daher nicht böse gemeint.
Das es prinzipiell geht, mit einer Hand voll casts oder ifs, ist, denke
ich unstrittig.
Wilhelm M. schrieb:> Johann L. schrieb:>> Dieter R. schrieb:>>> W.S. schrieb:>>>>>>> Egal, was man für Zahlendarstellungen wählt (int, long, int64, float,>>>> double), wird es immer einen gültigen Wert für a geben, der zusammen mit>>>> b zu einem Überlauf oder einer NAN führt.>>>> Nope. Ich habe oben eine Lösung für alle möglichen Eingabewerte>> gezeigt, die auch keinen breiteren Datentyp braucht.>>>> Beispiel Fall b >= 0 und c >= a: Es soll getestet werden, ob c in [a,>> a+b] gilt.>> [...]>> Das Dumme ist nur, dass dieser Beweis unnötig ist, da es zu demselben> Maschinencode führt wie meine Variante (mit Ausnahme 64Bit). Wobei die> template-Variante natürlich den an die Typen korrekt angepassten Code> liefert (kleiner).
Ich verstehe dein Lösung ehrlich gesagt nicht. mUpper wird doch als
int+int berechnet, selbst wenn enclosing_type_t int64_t zurückliefert,
weil der Aufruf sowas ist wie
ClosedOrderedInterval{a, a+b}.contains (c);
Wenn der Typ von a+b bestimmt wird ist es doch schon zu spät.
Johann L. schrieb:> Wilhelm M. schrieb:>> Johann L. schrieb:>>> Dieter R. schrieb:>>>> W.S. schrieb:>>>>>>>>> Egal, was man für Zahlendarstellungen wählt (int, long, int64, float,>>>>> double), wird es immer einen gültigen Wert für a geben, der zusammen mit>>>>> b zu einem Überlauf oder einer NAN führt.>>>>>> Nope. Ich habe oben eine Lösung für alle möglichen Eingabewerte>>> gezeigt, die auch keinen breiteren Datentyp braucht.>>>>>> Beispiel Fall b >= 0 und c >= a: Es soll getestet werden, ob c in [a,>>> a+b] gilt.>>>> [...]>>>> Das Dumme ist nur, dass dieser Beweis unnötig ist, da es zu demselben>> Maschinencode führt wie meine Variante (mit Ausnahme 64Bit). Wobei die>> template-Variante natürlich den an die Typen korrekt angepassten Code>> liefert (kleiner).>> Ich verstehe dein Lösung ehrlich gesagt nicht. mUpper wird doch als> int+int berechnet, selbst wenn enclosing_type_t int64_t zurückliefert,> weil der Aufruf sowas ist wie>> ClosedOrderedInterval{a, a+b}.contains (c);>> Wenn der Typ von a+b bestimmt wird ist es doch schon zu spät.
Ja, dass soll ja auch den Fehlerfall mit Overflow zeigen. Hier ist die
Verantwortung beim Caller, der Caller hätte hier auch enclosing_t<>
verwenden müssen, wie bei jeder Art der Arithmetik, wenn die
Wertebereiche maximal sein können.
Könnte auch verbessern mit uint_ranged<T, lower, upper>.
Die Klasse ClosedIntervall vermeidet das, dafür muss aber bspw. für
int64_t der Typ int128_t zur Verfügung stehen. Das ist Deine Lösung dann
besser.
Wilhelm M. schrieb:> Johann L. schrieb:>> Ich verstehe dein Lösung ehrlich gesagt nicht. mUpper wird doch als>> int+int berechnet, selbst wenn enclosing_type_t int64_t zurückliefert,>> weil der Aufruf sowas ist wie>>>> ClosedOrderedInterval{a, a+b}.contains (c);>>>> Wenn der Typ von a+b bestimmt wird ist es doch schon zu spät.>> Ja, dass soll ja auch den Fehlerfall mit Overflow zeigen. Hier ist die> Verantwortung beim Caller, der Caller hätte hier auch enclosing_t<>> verwenden müssen,
Die Idee dabei war doch, es sicher zu machen. Aber die Verantwortung
wird wieder an den Anwender abgeschoben.
Was ist eigentlich enclosing_type_t<int> ? Die kleinste Obermenge von
int ist int. Oder geht das implizit davon aus, dass eine Addition ohne
Überlauf stattfinden können soll und es ist int64_t? In dem Fall muss
der Anwender dann a und b auf int64_t casten bevor er sie in die
Funktion gibt.
> wie bei jeder Art der Arithmetik, wenn die> Wertebereiche maximal sein können.> Könnte auch verbessern mit uint_ranged<T, lower, upper>.
Was ist das nun wieder?
> Die Klasse ClosedIntervall vermeidet das, dafür muss aber bspw. für> int64_t der Typ int128_t zur Verfügung stehen. Da ist Deine Lösung dann> besser.
Und für mLower, mUpper muss doch auch int64_t genommen werden, damit
mUpper = mLower + w nicht überläuft?
Johann L. schrieb:> Und für mLower, mUpper muss doch auch int64_t genommen werden, damit> mUpper = mLower + w nicht überläuft?
Eigentlich ja nur für eine der beiden.
Heiko L. schrieb:> Johann L. schrieb:>> Und für mLower, mUpper muss doch auch int64_t genommen werden, damit>> mUpper = mLower + w nicht überläuft?>> Eigentlich ja nur für eine der beiden.
Für die linke Seite und mindestens eines der rechten Seite. Wenn nur
für einen Wertt int64_t genommen wird hat man int = int_64_t + int oder
int64_t = int + int und in beiden Fällen Überlauf.
Johann L. schrieb:> Heiko L. schrieb:>> Johann L. schrieb:>>> Und für mLower, mUpper muss doch auch int64_t genommen werden, damit>>> mUpper = mLower + w nicht überläuft?>>>> Eigentlich ja nur für eine der beiden.>> Für die linke Seite und mindestens eines der rechten Seite. Wenn nur> für einen Wertt int64_t genommen wird hat man int = int_64_t + int oder> int64_t = int + int und in beiden Fällen Überlauf.
Ja, aber dafür kapselt man den code da ja auch. Für die eine Seite
genügt immer der initiale Datentyp selbst. Würde den Speicherverbrauch
um 25% senken, wenn das Ding mal auf dem Stack landet. Das Problem hat
man sicher nicht, wenn man das als Funktion ausführt.
Wilhelm M. schrieb:> Dann hast Du also auch in den nächsten zwei Jahren nichts gelernt ...> schade - für Dich.
Warum sollte ich C++ lernen, wenn ich es nicht benutze? Ich habe doch
Sprachen aufgeführt die ich zur Lösung meiner Probleme benutze und damit
bin ich bisher sehr gut gefahren - naja C nur weil es für µC da nur
wenige brauchbare Alternativen gibt.
Achja beruflich muß ich mich mit C# noch auseinandersetzen. Ich braue
wirklich kein drittes C.
Frank M. schrieb:> Für einen sterblichen C-Programmierer ist das template-Gedöns absolut> kryptisch.
Lass man der W begreift das nicht. Er erwartet das andere sich mit
seinem Gedöns befassen, schaut aber selbst nicht über den Tellerrand
seiner C++ Suppe - ist vielleicht auch nicht möglich, weil ihn seine
streng geheimen Codeteile, die vielleicht zur Erhellung seines
geposteten Codes beitragen würden, ihn immer wieder nach unten ziehen.
Johann L. schrieb:> Wenn der Typ von a+b bestimmt wird ist es doch schon zu spät.
Genau so ist. Sobald die Addition a+b ausgeführt ist liegt alles im
Argen. ES muß vor der Addition geprüft werden ob's passt.
Z.B. so:
1
if(b<=MAX_INT-a){.....}
Das funktioniert für alle gültigen b von MIN_INT bis MAX_INT.
Randbedingung ist a >= 0. Für a und b < 0 muß man es zusätzlich prüfen,
da ansonsten MIN_INT überschritten werden könnte.
Da Differenzen (c-a) bzw. (a-c) im gültigen Unsigned-Bereich liegen muss
nicht mehr auf Über-/Unterlauf geprüft werden.
In der Funktion von Wilhelm ist mir allerdings nicht klar (1) wozu der
Fall-Through der beiden if-Bedingungen gut sein soll. (2) Falls int16_t
kein int ist, kommt es zu Promotions. (3) Erst wird (c-a) berechnet und
dann negiert. (Ist nicht falsch, aber etwas Overhead ;-)
Hier "meine" Variante davon:
1
2
boolinRange(inta,intb,intc)
3
{
4
if(b>=0)
5
{
6
// [a,...]
7
if(c<a)returnfalse;
8
9
// we want to return (c <= a+b): but (a+b) may overflow (UB)
10
// solution:
11
// since (c >= a) we can compute (c-a) in the unsigned range
12
// i.e.: (c <= a+b) <==> (c-a <= b)
13
return(unsigned)c-(unsigned)a<=(unsigned)b;
14
}
15
else
16
{
17
// [...,a]
18
if(a<c)returnfalse;
19
20
// we want to return (a+b <= c); but (a+b) may underflow (UB)
21
// solution:
22
// since (a >= c) we can compute (a-c) in the unsigned range
23
// i.e. (a+b <= c) <==> (a-c <= -b)
24
return(unsigned)a-(unsigned)c<=-(unsigned)b;
25
}
26
}
Ein paar Pointer:
* unsigned cast u for a signed x<0: u(x) = UINT_MAX+x [C99 § 6.3.1.3 /
2]
* a computation involving unsigned operands can never overflow, because
a result that cannot be represented by the resulting unsigned integer
type is reduced modulo the number that is one greater than the largest
value that can be represented by the resulting type. [C99 § 6.2.5 / 9]
* negation (-) for an unsigned u: -(u) + u = 0 [fehlt mir gerade die
Referenz]
Das Schöne an Wilhelms Lösung ist, dass man sie sprunglos umschreiben
kann:
Johann L. schrieb:> Nope. Ich habe oben eine Lösung für alle möglichen Eingabewerte> gezeigt, die auch keinen breiteren Datentyp braucht.
Also wenn Du Deine Lösung nicht zeigen willst, dann sage bitte
wenigstens, ob Du diese meinst, oder ob es überhaupt in diesem Thread
war, oder ob Du es nur verbal (ohne C-Code) beschrieben hast:
Johann L. schrieb:> bool in_range (int16_t c, int16_t a, int16_t b)> {> uint16_t c_a = (uint16_t) c - (uint16_t) a;>> if (b >= 0 && c >= a)> return c_a <= (uint16_t) b;>> if (b <= 0 && c <= a)> return -c_a <= - (uint16_t) b;>> return false;> }
Denn weder funktioniert sie (selbst bei 16-Bit-Integern), noch ist sie
irgendwie kürzer/schneller als naiv hingeschriebene ohne casts. Oder
gibt es hier eine berichtigte Version, die ich übersehen habe? (jetzt
von 32-Bit Integern mal abgesehen)
A. S. schrieb:> Also wenn Du Deine Lösung nicht zeigen willst, dann sage bitte> wenigstens, ob Du diese meinst
Die von dir zitierte mit diesem Bugfix:
Johann L. schrieb:> GRMPF. Also noch mehr Casts:> return (uint16_t) -c_a <= (uint16_t) - (uint16_t) b;> Damit es bei -b kein signed-Overflow gibt falls b = INT16_MIN = INT_MIN.
Mikro 7. schrieb:> Ich habe jetzt auch Wilhelms Lösung gesehen (die Beiträge sind kaum zu> überschauen).>> Zuerst nochmal der "Straight-Forward" Ansatz von A. S.:>
1
>boolinRange(inta,intb,intc)
2
>{
3
>if(b>=0)
4
>{
5
>// min check for c in [a,...]
6
>if(c<a)returnfalse;
7
>
8
>// c is always in [a,>INT_MAX]
9
>if(a>INT_MAX-b)returntrue;
10
>
11
>// c in [a,a+b] w/o overflow (UB)
12
>returnc<=a+b;
13
>}
14
>else
15
>{
16
>// max check for c in [...,a]
17
>if(a<c)returnfalse;
18
>
19
>// c is always in [<INT_MIN,a]
20
>if(a<INT_MIN-b)returntrue;
21
>
22
>// [a+b,a] w/o underflow (UB)
23
>returna+b<=c;
24
>}
25
>}
Gefällt mir bisher am besten. Hat zwar 2 Vergleiche mehr als meine
Version, aber der besseren Nachvollziehbarkeit würde ich hier klar den
Vorzug geben.
A. S. schrieb:> Johann L. schrieb:>> Ich habe oben eine Lösung für alle möglichen Eingabewerte gezeigt,>> die auch keinen breiteren Datentyp braucht.>> Dann zitiere doch bitte die Version, die funktioniert. Ich finde keine> von Dir.Beitrag "Re: C: Prüfen, ob Variable in Intervall"
Für den Fall dass der Typ < int ist kommen aber noch 3 Casts hinzu (gilt
ebenso für die von Mikro 7 zitierten Codes):
1
#include<type_traits>
2
3
// c in [a, a+b] u [a+b, a]
4
5
template<typenameT>
6
boolin_range(Tc,Ta,Tb)
7
{
8
static_assert(std::is_integral<T>(),"T must be an integer type");
9
static_assert(std::is_signed<T>(),"T must be signed");
10
usingU=typenamestd::make_unsigned<T>::type;
11
12
Uc_a=(U)((U)c-(U)a);
13
14
if(b>=0&&c>=a)
15
// We have 0 <= c - a < 2^N and 0 <= b < 2^{N-1} for N-bit
16
// arithmetic and type T. Hence the following comparison is
17
// just c - a <= b i.e. c <= a + b.
18
returnc_a<=(U)b;
19
20
if(b<0&&c<=a)
21
// We have 0 <= a - c < 2^N and 0 < -b <= 2^{N-1}. Hence the
22
// following comparison is just -c + a <= -b i.e. c >= a + b.
23
return(U)-c_a<=(U)-(U)b;
24
25
returnfalse;
26
}
Übrigens geht das genauso wenn b Unsigned ist. Einfach alle Vergleiche
falten die b enthalten. 0 <= b < 2^N ist für den 1. Fall b >= 0 als
Begründung ausreichend, und der 2. Fall entfällt.
Und falls a und c beide Unsigned sind bleiben Begründung und Code auch
erhalten.
Erst falls a und c Signed und Unsigned mischen, muss man einen größeren
Typ nehmen.
Johann L. schrieb:> Mikro 7. schrieb:>> Ich habe jetzt auch Wilhelms Lösung gesehen (die Beiträge sind kaum zu>> überschauen).>>>> Zuerst nochmal der "Straight-Forward" Ansatz von A. S.:>>
1
>>boolinRange(inta,intb,intc)
2
>>{
3
>>if(b>=0)
4
>>{
5
>>// min check for c in [a,...]
6
>>if(c<a)returnfalse;
7
>>
8
>>// c is always in [a,>INT_MAX]
9
>>if(a>INT_MAX-b)returntrue;
10
>>
11
>>// c in [a,a+b] w/o overflow (UB)
12
>>returnc<=a+b;
13
>>}
14
>>else
15
>>{
16
>>// max check for c in [...,a]
17
>>if(a<c)returnfalse;
18
>>
19
>>// c is always in [<INT_MIN,a]
20
>>if(a<INT_MIN-b)returntrue;
21
>>
22
>>// [a+b,a] w/o underflow (UB)
23
>>returna+b<=c;
24
>>}
25
>>}
>> Gefällt mir bisher am besten. Hat zwar 2 Vergleiche mehr als meine> Version, aber der besseren Nachvollziehbarkeit würde ich hier klar den> Vorzug geben.>> A. S. schrieb:>> Johann L. schrieb:>>> Ich habe oben eine Lösung für alle möglichen Eingabewerte gezeigt,>>> die auch keinen breiteren Datentyp braucht.>>>> Dann zitiere doch bitte die Version, die funktioniert. Ich finde keine>> von Dir.>> Beitrag "Re: C: Prüfen, ob Variable in Intervall">> Für den Fall dass der Typ < int ist kommen aber noch 3 Casts hinzu (gilt> ebenso für die von Mikro 7 zitierten Codes):>>
1
#include<type_traits>
2
>
3
>// c in [a, a+b] u [a+b, a]
4
>
5
>template<typenameT>
6
>boolin_range(Tc,Ta,Tb)
7
>{
8
>static_assert(std::is_integral<T>(),"T must be an integer type");
9
>static_assert(std::is_signed<T>(),"T must be signed");
10
>usingU=typenamestd::make_unsigned<T>::type;
11
>
12
>Uc_a=(U)((U)c-(U)a);
13
>
14
>if(b>=0&&c>=a)
15
>// We have 0 <= c - a < 2^N and 0 <= b < 2^{N-1} for N-bit
16
>// arithmetic and type T. Hence the following comparison is
17
>// just c - a <= b i.e. c <= a + b.
18
>returnc_a<=(U)b;
19
>
20
>if(b<0&&c<=a)
21
>// We have 0 <= a - c < 2^N and 0 < -b <= 2^{N-1}. Hence the
22
>// following comparison is just -c + a <= -b i.e. c >= a +
23
>b.
24
>return(U)-c_a<=(U)-(U)b;
25
>
26
>returnfalse;
27
>}
>> Übrigens geht das genauso wenn b Unsigned ist. Einfach alle Vergleiche> falten die b enthalten. 0 <= b < 2^N ist für den 1. Fall b >= 0 als> Begründung ausreichend, und der 2. Fall entfällt.>> Und falls a und c beide Unsigned sind bleiben Begründung und Code auch> erhalten.>> Erst falls a und c Signed und Unsigned mischen, muss man einen größeren> Typ nehmen.
Ich kann es kaum fassen, was meine Augen da sehen ;-) Wo ist der Hinweis
auf Code-Bloat, und ich verstehe den ganzen Kram nicht, viel zu
abstrakt, bla, bla, bla.
Vielleicht hat die beharrliche Diskussion ja doch etwas genutzt. Würde
mich ja freuen - immer noch ungläubig.
Nur das Interface ist noch unschön, denn was bedeutet:
1
in_range(1,0,3);
etwa: 1 in [0,3] -> false
oder: 3 in [1, 1] -> false
Ein Interface sollte leicht richtig, und schwer falsch zu benutzen sein.
Das ist hier noch nicht erfüllt.
Das hatte ich in meinem Code ebenfalls adressiert. Leider ist es hier
wieder herausgefallen.
Johann L. schrieb:> Gefällt mir bisher am besten.
Oh, vielen Dank!
Johann L. schrieb:> bool in_range (int16_t c, int16_t a, int16_t b)
Sorry, ich hatte da einen Fehler im Kopf eingebaut, beim Verifizieren.
Dein (korrigierter) Code funktioniert. Richtig und ohne UB.
Wilhelm M. schrieb:> Ich kann es kaum fassen, was meine Augen da sehen ;-) Wo ist der Hinweis> auf Code-Bloat,
Du bezeichnest 7 Zeilen Code als Bloat?
Dein Code hat wohl deutlich mehr, den Code hast du noch nichtmal
gezeigt. Ich möchte mich ehrlich gesagt auch nicht durch irgendwelche
verlinkten Projekte im Netz durchwühlen müssen um was funktionsfähigen
zu bekommen.
Das verlinkte Projekt hat vermutlich mehr als 7 Zeilen Code...
> und ich verstehe den ganzen Kram nicht, viel zu> abstrakt, bla, bla, bla.
Es geht natürlich auch weniger abstrakt ohne type_trais, indem man
direkt z.B. int / unsigned einsetzt oder welchen Typ man eben braucht.
Johann L. schrieb:> Wilhelm M. schrieb:>> Ich kann es kaum fassen, was meine Augen da sehen ;-) Wo ist der Hinweis>> auf Code-Bloat,>> Du bezeichnest 7 Zeilen Code als Bloat?>>> und ich verstehe den ganzen Kram nicht, viel zu>> abstrakt, bla, bla, bla.>> Es geht natürlich auch weniger abstrakt ohne type_trais, indem man> direkt z.B. int / unsigned einsetzt oder welchen Typ man eben braucht.
Oder auch abstrakter: Die static_asserts sind in dem Beispiel eher
schlecht, weil sich - man staune - Intervalle auch mit vorzeichenlosen
Typen problemlos denken lassen. In dem Fall sollte der Fehler nicht
heißen: "Intervalle müssen Typen mit Vorzeichen haben".
Und warum sollte es keine Intervalle mit Fließkommazahlen geben?
Johann L. schrieb:> Wilhelm M. schrieb:>> Ich kann es kaum fassen, was meine Augen da sehen ;-) Wo ist der Hinweis>> auf Code-Bloat,>> Du bezeichnest 7 Zeilen Code als Bloat?
Ich nicht. Das kommt doch normalerweise von der hier anwesenden
Anti-C++-Gemeinde.
> Dein Code hat wohl deutlich mehr, den Code hast du noch nichtmal> gezeigt.
Ich habe den gesamten Code oben in den Compiler-Explorer verlinkt, inkl.
Deiner und der Ur-Variante.
> Ich möchte mich ehrlich gesagt auch nicht durch irgendwelche> verlinkten Projekte im Netz durchwühlen müssen um was funktionsfähigen> zu bekommen.
Einfacher geht es nicht (s.o.). Du brauchst gar nichts zu tun. Und es
nur genau das Beispiel inkl. seiner Varianten.
Walter T. schrieb:> Der Betrag> von b ist üblicherweise sehr viel kleiner als INT_MAX
Gut dass sich die C-Gemeinde nun geeinigt hat.
Möchte noch einmal auf diese Bemerkung zurückkommen. Habe mal
überschlagen, welche Werte von b den Wertebereich nicht verlassen.
1. S-Int-8
c im Intervall, b in [1, 22]
c ausserhalb Intervall, b in [1, 7]
2. S-Int-16
c im Intervall, b in [1, 362]
c ausserhalb Intervall, b in [1, 128]
Damit scheint mir der Bereich, in dem die Formel "funktioniert", doch
sehr eingeschränkt zu sein. Und wenn der TO später schreibt, dass er
einen sehr genauen Erwartungswert für c hat, dann würde man doch einfach
wieder Subtrahieren, um zu sehen, wie nahe der Wert wirklich ist oder?!
Gruß Rainer
Rainer V. schrieb:> Und wenn der TO später schreibt, dass er> einen sehr genauen Erwartungswert für c hat, dann würde man doch einfach> wieder Subtrahieren, um zu sehen, wie nahe der Wert wirklich ist oder?!
Ist das nicht seine Aufgabenstellung? Hörts sich irgendwie sehr ähnlich
an. Und einfacher.
a - alter Wert
c - neuer Wert
b - maximale Abweichung (in beide Richtungen)
Die Bedingung zu prüfen:
abs(c-a)<=b
zitter_ned_aso schrieb:> Ist das nicht seine Aufgabenstellung? Hörts sich irgendwie sehr ähnlich> an. Und einfacher.
Ursprünglich konnte er nicht schlafen wegen "((a-c)*(a+b-c)) <= 0" da
war die simple Subtraktion noch nicht gefragt :-)
Gruß Rainer