Forum: Mikrocontroller und Digitale Elektronik C: Prüfen, ob Variable in Intervall


von Walter T. (nicolas)


Lesenswert?

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
bool isdrin(int a, int b, int c)
3
{
4
    return ((a-c)*(a+b-c)) <= 0;
5
}

Gibt es eine schönere Lösung?

von c-hater (Gast)


Lesenswert?

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
> bool isdrin(int a, int b, int c)
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.

von Erfahrener Entwickler (Gast)


Lesenswert?

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?

von Dergute W. (derguteweka)


Lesenswert?

Moin,

Vielleicht kann man die Multiplikation im Beispiel noch durch eine 
Addition von Logarithmen ersetzen. Kommt sicher gut in Lesbarkeit und 
Performance.

SCNR
WK

von Egon D. (Gast)


Lesenswert?

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.

von Egon D. (Gast)


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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
bool in_range (int a, int b, int c)
4
{
5
    if (c >= a && b >= 0)
6
        return (unsigned) c - (unsigned) a <= (unsigned) b;
7
    else
8
        return false;
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.

von c-hater (Gast)


Lesenswert?

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...

von Egon D. (Gast)


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Johann L. schrieb:
> Eine korrektte Lösung:
>
>
1
> // c in [a, a + b]  <=>  c - a in [0, b]
2
> 
3
> bool in_range (int a, int b, int c)
4
> {
5
>     if (c >= a && b >= 0)
6
>         return (unsigned) c - (unsigned) a <= (unsigned) b;
7
>     else
8
>         return false;
9
> }


...und falls 2-er Komplement negative Zahlen darstellt geht auch kürzer:
1
bool in_range (int a, int b, int c)
2
{
3
    return b >= 0 && (unsigned) c - (unsigned) a <= (unsigned) b;
4
}

von Egon D. (Gast)


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Ganz analog kann man für ARM auch den unsigned-Fall behandeln falls b 
klein ist:
1
bool in_range (unsigned a, unsigned b, unsigned c)
2
{
3
    assert (b <= (unsigned) INT_MAX);
4
5
    return c - a <= b;
6
}

von c-hater (Gast)


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

c-hater schrieb:
> Johann L. schrieb:
>
>> Eine korrektte Lösung:
>
> [...]
>
> Nein, korrekt ist die auch nicht (höchstens rein formal).

Was daran ist falsch?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

...tatsächlich ist es genau der Code, den GCC für folgende Vergleiche 
erzeugt:
1
void f (void);
2
3
void test (int c)
4
{
5
    if (c >= 50 && c <= 100)
6
        f();
7
}
Hier avr-gcc:
1
test:
2
  sbiw r24,50
3
  sbiw r24,51
4
  brsh .L1
5
  jmp f
6
.L1:
7
  ret
Anstatt 2 Vergleichen gibt es 1 Vergleich und 1 Subtraktion mit 
wrap-around wenn c < 50 ist.

von c-hater (Gast)


Lesenswert?

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!

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

c-hater schrieb:
> Asm rules!

Schreibt jemand, der ein Typsystem unzureichend findet...

: Bearbeitet durch User
von zitter_ned_aso (Gast)


Lesenswert?

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.

von Zeno (Gast)


Lesenswert?

Ich würde es so lösen:
1
#define max(a,b)            (((a) > (b)) ? (a) : (b))
2
#define min(a,b)            (((a) < (b)) ? (a) : (b))
3
4
bool isdrin(int a, int b, int c)
5
{
6
    return (c>=min(a, a+b) && c<=max(a, a+b));
7
}

von Egon D. (Gast)


Lesenswert?

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?

von Walter T. (nicolas)


Lesenswert?

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
bool isdrin(int a, int b, int c)
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.

: Bearbeitet durch User
von PittyJ (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

von Oliver S. (oliverso)


Lesenswert?

Walter T. schrieb:
> Zeig mal! Gerne in der Nicht-C-Sprache Deiner Wahl.

Gemeint ist ja wohl sowas hier:
1
bool result(int a, int ab, int c) {
2
    return ((c >= a  && c <= ab) ||
3
            (c >= ab && c <=  a));
4
5
}

So viel ineffizienter als die Lösung von Johann ist das jetzt auch 
nicht.

https://godbolt.org/z/pldTZA

Oliver

von zitter_ned_aso (Gast)


Lesenswert?

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.

von Zeno (Gast)


Lesenswert?

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.

von c-heater (Gast)


Lesenswert?

1
return (c >= a) == (c <= (a+b));

von Zeno (Gast)


Lesenswert?

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.

von Oliver S. (oliverso)


Lesenswert?

Knapp daneben ist auch vorbei.

Oliver

von Rolf M. (rmagnus)


Lesenswert?

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.

von Yalu X. (yalu) (Moderator)


Lesenswert?

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:

1
return ((c > a) == (c < a + b)) | (c == a) | (c == a + b);

: Bearbeitet durch Moderator
von A. S. (Gast)


Lesenswert?

Zeno schrieb:
> Mit Minima und Maxima funktioniert es definitiv.

Dann schreib es doch hin, mit min() und max().

von Zeno (Gast)


Lesenswert?

A. S. schrieb:
> Dann schreib es doch hin, mit min() und max().

Du kannst lesen?

Beitrag "Re: C: Prüfen, ob Variable in Intervall"

von P. S. (namnyef)


Lesenswert?

Warum nicht einfach so etwas in dieser Art?
1
bool isInRange(int a, int b, int c)
2
{
3
    int upper_bound = 0;
4
    int lower_bound = 0;
5
    bool ret = false;
6
    
7
    // assign intervall boundaries
8
    if (a > a + b)
9
    {
10
        upper_bound = a;
11
        lower_bound = a + b;
12
    }
13
    else
14
    {
15
        upper_bound = a + b;
16
        lower_bound = a;
17
    }
18
19
    // if c out of intervall
20
    if (c > upper_bound || c < lower_bound)
21
    {
22
        ret = false;
23
    }
24
    else
25
    {
26
        ret = true;
27
    }
28
    
29
    return ret;
30
}

Optional halt noch gegen Überläufe absichern.

: Bearbeitet durch User
von Zeno (Gast)


Lesenswert?

P. S. schrieb:
> Warum nicht einfach so etwas in dieser Art?

Noch mehr Zeilen gehen nicht?

von Stefan F. (Gast)


Lesenswert?

Zeno schrieb:
> Noch mehr Zeilen gehen nicht?

Das ist nicht schlimm, sofern der Code gut lesbar ist.

von P. S. (namnyef)


Lesenswert?

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.

von Erfahrener Entwickler (Gast)


Lesenswert?

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!
1
  return lower_bound <= c && c <= upper_bound;

von Stefan F. (Gast)


Lesenswert?

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.

von Erfahrener Entwickler (Gast)


Lesenswert?

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.

von Oliver S. (oliverso)


Lesenswert?

Erfahrener Entwickler schrieb:
> Das ist halt falsch.

Nochmal zurück auf los, und den Quelltext nochmals lesen und verstehen.
Das wird schon...

Oliver

von mh (Gast)


Lesenswert?

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.

von zitter_ned_aso (Gast)


Lesenswert?

Oliver S. schrieb:
> https://godbolt.org/z/pldTZA


ich habe dort
1
bool isdrin1(int a, int b, int c)
2
{
3
    return ((a-c)*(a+b-c)) <= 0;
4
}
5
6
7
bool isdrin2(int a, int b, int c)
8
{
9
    return ((a-c)*((a-c)+b)) <= 0;
10
}

eingegeben. Wenn man (a-c) klammert, dann spart man eine Subtraktion.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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
bool in_range (int16_t c, int16_t a, int16_t b)
4
{
5
    uint16_t c_a = (uint16_t) c - (uint16_t) a;
6
7
    if (b >= 0 && c >= a)
8
        return c_a <= (uint16_t) b;
9
10
    if (b <= 0 && c <= a)
11
        return -c_a <= - (uint16_t) b;
12
    
13
    return false;
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.

von Zeno (Gast)


Lesenswert?

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.

von Jemand (Gast)


Lesenswert?

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".

von Stefan F. (Gast)


Lesenswert?

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?:
1
    // assign intervall boundaries
2
    if (a > a + b)
3
    {
4
        upper_bound = a;
5
        lower_bound = a + b;
6
    }
7
    else
8
    {
9
        upper_bound = a + b;
10
        lower_bound = a;
11
    }

Für mich nicht.

von Jemand (Gast)


Lesenswert?

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.

von Zeno (Gast)


Lesenswert?

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.

von Carl D. (jcw2)


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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 :-)

von Rolf M. (rmagnus)


Lesenswert?

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.

von Carl D. (jcw2)


Lesenswert?

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 ;-)

von Oliver S. (oliverso)


Lesenswert?

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:
1
#include <algorithm>
2
bool inBoundaries1(int a, int b, int c)
3
{
4
    int sumBoundary = a+b;
5
    if (a > sumBoundary)
6
        std::swap(a, sumBoundary);
7
8
    return (c<a || c>sumBoundary);
9
}

Oliver

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Oliver S. schrieb:
> #include <algorithm>

> std::swap(a, sumBoundary);

Gibt's in C nicht.

von Oliver S. (oliverso)


Lesenswert?

Rolf M. schrieb:
> Gibt's in C nicht.

Von C war auch nie die Rede. Nur von ARM-gcc, und das bedeutet doch 
hoffentlich C++.

Oliver

von Rolf M. (rmagnus)


Lesenswert?

Oliver S. schrieb:
> Von C war auch nie die Rede.

Doch, war es:

Walter T. schrieb:
> C: Prüfen, ob Variable in Intervall

von Walter T. (nicolas)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

Beitrag #5914523 wurde von einem Moderator gelöscht.
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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_t c_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.

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

von Jemand (Gast)


Lesenswert?

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.

von P. S. (namnyef)


Lesenswert?

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:
1
bool isInRange(int a, int b, int c)
2
{
3
    int upper_bound = 0;
4
    int lower_bound = 0;
5
    
6
    // assign intervall boundaries
7
    if (a > a + b)
8
    {
9
        upper_bound = a;
10
        lower_bound = a + b;
11
    }
12
    else
13
    {
14
        upper_bound = a + b;
15
        lower_bound = a;
16
    }
17
18
    // if c out of intervall
19
    if (c > upper_bound || c < lower_bound)
20
    {
21
        return false;
22
    }
23
    else
24
    {
25
        return true;
26
    }
27
}

: Bearbeitet durch User
von A. S. (Gast)


Lesenswert?

War der schon?
1
 return c==a||c==a+b||c<a^c<a+b;

Nur mit Oders ;-)

von zitter_ned_aso (Gast)


Lesenswert?

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
1
bool isdrin(int a, int b, int c)
2
{
3
    int x=(a-c);
4
    int y=(a-c)+b;
5
6
7
    int sign_x = (x>0)-(x<0);
8
    int sign_y=(y>0)-(y<0);
9
10
11
    return (sign_x*sign_y) <= 0;
12
}

von Yalu X. (yalu) (Moderator)


Lesenswert?

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
  return c >= lower_bound && c <= upper_bound;

schreiben.

Wenn man möchte, kann man den linken Teilausdruck umdrehen

1
  return lower_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.

: Bearbeitet durch Moderator
von P. S. (namnyef)


Lesenswert?

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.

Beitrag #5914926 wurde von einem Moderator gelöscht.
von Egon D. (Gast)


Lesenswert?

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
1
 
2
a=a+7;
auch nicht, oder?

Beitrag #5915006 wurde vom Autor gelöscht.
von Wilhelm M. (wimalopaan)


Lesenswert?

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
> bool isdrin(int a, int b, int c)
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++:
1
template<typename T, typename U>
2
requires (std::is_integral_v<T> && std::is_integral_v<U> && ((std::is_signed_v<T> && std::is_signed_v<T>) || (std::is_unsigned_v<T> && std::is_unsigned_v<U>)))
3
struct ClosedOrderedInterval {
4
    using value_type = containing_type_t<T, U>;
5
    constexpr ClosedOrderedInterval(T lower, U upper) : mLower{lower}, mUpper{upper} {
6
        if (mLower > mUpper) {
7
            using std::swap;
8
            swap(mLower, mUpper);
9
        }
10
        assert(mLower <= mUpper);
11
    }
12
    constexpr bool contains(T value) const {
13
        return (value >= mLower) && (value <= mUpper);
14
    }
15
    private:
16
    value_type mLower;
17
    value_type mUpper;
18
};
19
20
21
int main() {
22
    std::cout << std::boolalpha;
23
    
24
    using value_type = int;
25
    
26
    value_type a{0};
27
    value_type b{0};
28
    value_type c{0};
29
    
30
    std::cout << ClosedOrderedInterval{0, 2}.contains(1) << '\n';
31
32
    a = 2;
33
    b = -1;
34
    std::cout << ClosedOrderedInterval{a, etl::enclosing_t<value_type>{a} + b}.contains(c) << '\n';
35
    c = 1;    
36
    std::cout << ClosedOrderedInterval{etl::enclosing_t<value_type>{a} + b, a}.contains(c) << '\n';
37
38
    a = std::numeric_limits<int>::max() - 10;
39
    b = 20;
40
    
41
    std::cout << ClosedOrderedInterval{a, a + b}.contains(c) << '\n';
42
    std::cout << ClosedOrderedInterval{a, etl::enclosing_t<value_type>{a} + b}.contains(c) << '\n';
43
}

von Stefan F. (Gast)


Lesenswert?

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.

von Zeno (Gast)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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;

von Wilhelm M. (wimalopaan)


Lesenswert?

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 
...

von Rolf M. (rmagnus)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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!

von Wilhelm M. (wimalopaan)


Lesenswert?

Alternativ zu der oben gezeigten Lösung könnte ich mir folgendes auch 
noch vorstellen:
1
template<typename T>
2
struct ClosedInterval {
3
    using value_type = etl::enclosing_t<T>;
4
    explicit constexpr ClosedInterval(T left) : mLower{left} {}
5
    const ClosedInterval& width(T w) {
6
        mUpper = mLower + w;
7
        if (mLower >= mUpper) {
8
            using std::swap;
9
            swap(mLower, mUpper);
10
        }
11
        return *this;
12
    }
13
    constexpr bool contains(T value) const {
14
        return (value >= mLower) && (value <= mUpper);
15
    }
16
private:
17
    value_type mLower{0};
18
    value_type mUpper{0};
19
};

von Oliver S. (oliverso)


Lesenswert?

Wenn schon, dann doch gleich so:
1
bool isInRange{auto bound1, auto bound2, auto val)
2
{
3
   return ((c<=>bound1) * (c<=>bound2) < 0) || (c==bound1 && c==bound2);
4
}

Compiliert leider mit den aktuellen Versionen von g++ und clang noch 
nicht, daher ungetestet ;)

Oliver

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Oliver S. schrieb:
> Wenn schon, dann doch gleich so:
>
>
1
> bool isInRange{auto bound1, auto bound2, auto val)
2
> {
3
>    return ((c<=>bound1) * (c<=>bound2) < 0) || (c==bound1 && c==bound2);
4
> }
5
>
>
> Compiliert leider mit den aktuellen Veriosnen von g++ und clang noch
> nicht, daher ungetestet ;)

Ersetze noch c durch val ...

von Wilhelm M. (wimalopaan)


Lesenswert?

Oliver S. schrieb:
> Wenn schon, dann doch gleich so:
>
>
1
> bool isInRange{auto bound1, auto bound2, auto val)
2
> {
3
>    return ((c<=>bound1) * (c<=>bound2) < 0) || (c==bound1 && c==bound2);
4
> }
5
>
>
> 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.

von Oliver S. (oliverso)


Lesenswert?

Wilhelm M. schrieb:
> Wobei solche mehrstelligen Funktion mit gleichen / generischen Typen
> sehr problematisch sind.

Ist ja ok...
1
template<typename T, typename V, typename U>
2
bool isInRange(auto val, std::pair<V, U> UnOrderedBoundaries);

Oliver

von Wilhelm M. (wimalopaan)


Lesenswert?

Oliver S. schrieb:
> Wilhelm M. schrieb:
>> Wobei solche mehrstelligen Funktion mit gleichen / generischen Typen
>> sehr problematisch sind.
>
> Ist ja ok...
>
>
1
template<typename T, typename V, typename U>
2
> bool isInRange(auto val, std::pair<V, U> UnOrderedBoundaries);
3
>

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 ...

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Lukas (Gast)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Lukas (Gast)


Lesenswert?

Wilhelm M. schrieb:
> M.E. immer eine schlechte Idee. Optimierungen sind die vornehmste
> Aufgabe eines Compilers.

Deswegen auch der ";)" und der Absatz danach

von Yalu X. (yalu) (Moderator)


Lesenswert?

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?

von Zeno (Gast)


Lesenswert?

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
unsigned char isdrin(double a, double b, double c)
9
{
10
    return (min(a, a+b)<=c && c<= max(a, a+b));
11
}
12
13
int main(void) {
14
  int a, i;
15
  double b, 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
  return EXIT_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.

von Zeno (Gast)


Lesenswert?

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?

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

: Bearbeitet durch Moderator
von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Zeno (Gast)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

Zeno schrieb:

> und dann Code abliefern der von der Allgemeinheit nicht nachvollzogen
> werden kann.

Die Allgemeinheit kann vieles nicht nachvollziehen.

von Dieter R. (drei)


Lesenswert?

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.

von Yalu X. (yalu) (Moderator)


Lesenswert?

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:
1
#include <stdio.h>
2
3
void solve_problem(FILE *fpin, FILE *fpout);
4
5
int main(void) {
6
  solve_problem(stdin, stdout);
7
}

Der Aufruf erfolgt mit

1
solve_problem <problembeschreibung.txt >loesung.txt

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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

von Hugo E. (Gast)


Lesenswert?

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!

von Hugo E. (Gast)


Lesenswert?

Hugo E. schrieb:
> Wenn man als Vorgabe "min" kleiner macht als "max", muss false
> rauskommen!

Sorry, natürlich andersherum :-)

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

Zeno schrieb:

>
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
> unsigned char isdrin(double a, double b, double c)
9
> {
10
>     return (min(a, a+b)<=c && c<= max(a, a+b));
11
> }
12
>

Und Du möchtest tatsächlich immer mit double rechnen? Hattest nicht 
gerade Du die Bemerkung mit Performance, etc. gebracht. Interessant ...

von Wilhelm M. (wimalopaan)


Lesenswert?

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
1
template<typename T>
2
struct Width {
3
    explicit Width(T v) : value{v} {}
4
    const T value;
5
};
6
template<typename T>
7
struct Left {
8
    explicit Left(T v) : value{v} {}
9
    const T value;
10
};
11
12
template<typename T>
13
struct ClosedInterval {
14
    using value_type = etl::enclosing_t<T>;
15
16
    explicit constexpr ClosedInterval(T left) : mLower{left} {}
17
    
18
    explicit constexpr ClosedInterval(Left<T> left, Width<T> width) : mLower{left.value}, mUpper{left.value + width.value} {
19
        if (mLower >= mUpper) {
20
            using std::swap;
21
            swap(mLower, mUpper);
22
        }
23
    }
24
25
    const ClosedInterval& width(T w) {
26
        mUpper = mLower + w;
27
        if (mLower >= mUpper) {
28
            using std::swap;
29
            swap(mLower, mUpper);
30
        }
31
        return *this;
32
    }
33
    constexpr bool contains(T value) const {
34
        return (value >= mLower) && (value <= mUpper);
35
    }
36
private:
37
    value_type mLower{0};
38
    value_type mUpper{0};
39
};

und - viel wichtiger - von der Verwendung
1
    std::cout << ClosedInterval{a}.width(b).contains(c) << '\n';

ist für Dich kryptisch?

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

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 ;-)

von Zeno (Gast)


Lesenswert?

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
int a=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.

von Zeno (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Korrektheit ist auch nicht ganz unerheblich ;-)

Eben!

von Wilhelm M. (wimalopaan)


Lesenswert?

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
int a=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++.

von a_zip (Gast)


Lesenswert?

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

von Vincent H. (vinci)


Lesenswert?

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...

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

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 ;-)

von Zeno (Gast)


Lesenswert?

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.

von Egon D. (Gast)


Lesenswert?

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!

von Walter T. (nicolas)


Lesenswert?

Ich lese das jetzt seit zwei Tagen mit wachsender Verzweiflung mit. 
Kommt da noch ein "April April - wir haben uns alle abgesprochen!" ? 
Bitte?

von Egon D. (Gast)


Lesenswert?

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?

von Zeno (Gast)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Zeno (Gast)


Lesenswert?

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.

von Rainer V. (a_zip)


Lesenswert?

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

von Dieter R. (drei)


Lesenswert?

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.

von Heiko L. (zer0)


Lesenswert?

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.

von sid (Gast)


Lesenswert?

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.

von Heiko L. (zer0)


Lesenswert?

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...

von Rainer V. (a_zip)


Lesenswert?

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

von Dieter R. (drei)


Lesenswert?

Heiko L. schrieb:

> Die meisten Zahlen sind klein.

Ach. Nun sind wir endgültig bei Loriot gelandet? Oder sprichst du von 
meinem/deinem Konto?

von Rainer V. (a_zip)


Lesenswert?

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

von W.S. (Gast)


Lesenswert?

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.

von Egon D. (Gast)


Lesenswert?

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.

von Dieter R. (drei)


Lesenswert?

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.

von Egon D. (Gast)


Lesenswert?

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)?
1
a = 32'000
2
b =    760 
3
c =   -100 
4
5
(a-c) * (a+b-c) = (32'000-(-100)) * (32'000+760-(-100))
6
 = 31'900 * 32'860

Die Zahl 32'860 ist mit 16bit-Integer nicht darstellbar,
d.h. es gibt einen Überlauf.

von Yalu X. (yalu) (Moderator)


Lesenswert?

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
bool isInRange(int lower, int upper, int val) {
2
  return val >= 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.

von A. S. (Gast)


Lesenswert?

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:
1
if(b<0) {if(INT_MIN-b>a) {ab=INT_MIN;} else {ab=a+b;}}
2
else    {if(INT_MAX-b<a) {ab=INT_MAX;} else {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
return 0;

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.

von Rainer V. (a_zip)


Lesenswert?

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

von A. S. (Gast)


Lesenswert?

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:
1
if(b<0) {if(INT_MIN-b>a) {ab=INT_MIN;} else {ab=a+b;}}
2
else    {if(INT_MAX-b<a) {ab=INT_MAX;} else {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
return 0;

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.

von Mikro 7. (mikro77)


Lesenswert?

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
bool inRange(int a,int b,int c)
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?

: Bearbeitet durch User
von Egon D. (Gast)


Lesenswert?

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.

von Egon D. (Gast)


Lesenswert?

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.

von Jemand (Gast)


Lesenswert?

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.

von sid (Gast)


Lesenswert?

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.

von A. S. (Gast)


Lesenswert?

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?

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Walter T. (nicolas)


Lesenswert?

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
bool hasCrossed(int64_t x, int64_t u, int32_t deltaU)
8
{
9
    int32_t diff = 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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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` ...

von Walter T. (nicolas)


Lesenswert?

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?

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Dieter R. (drei)


Lesenswert?

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?

von Walter T. (nicolas)


Lesenswert?

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.

von A. S. (Gast)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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
        mov     eax, 32762
3
        mov     WORD PTR [rsp-2], ax
4
        movzx   eax, WORD PTR [rsp-2]
5
        cmp     ax, 32756
6
        setg    al
7
        movzx   eax, al
8
        ret

Deiner liefert:
1
isdrin(int, int, int):
2
        add     esi, edi
3
        sub     edi, edx
4
        sub     esi, edx
5
        imul    esi, edi
6
        test    esi, esi
7
        setle   al
8
        ret
9
main:
10
        mov     eax, 32762
11
        mov     ecx, 32777
12
        mov     WORD PTR [rsp-2], ax
13
        movsx   edx, WORD PTR [rsp-2]
14
        mov     eax, 32757
15
        sub     eax, edx
16
        sub     ecx, edx
17
        imul    eax, ecx
18
        test    eax, eax
19
        setle   al
20
        movzx   eax, al
21
        ret

Bei int32_t ist das Ergebnis, der Unterschied natürlich noch krasser 
(vorausgesetzt, man ändert Deine ursprüngliche Funktion dazu).

: Bearbeitet durch User
von Zeno (Gast)


Lesenswert?

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.

von Zeno (Gast)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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 ...

von Zeno (Gast)


Lesenswert?

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.

von Zeno (Gast)


Lesenswert?

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.

von Oliver S. (oliverso)


Lesenswert?

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

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Dieter R. (drei)


Lesenswert?

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.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch Moderator
von Yalu X. (yalu) (Moderator)


Lesenswert?

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
bool hasCrossed(int64_t x, int64_t u, int32_t deltaU)

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).

: Bearbeitet durch Moderator
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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).

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

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

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von A. S. (Gast)


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

Hier nochmal alles in einen Topf geworfen:

https://godbolt.org/z/ZnHiXN

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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?

von Heiko L. (zer0)


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch User
von Heiko L. (zer0)


Lesenswert?

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.

: Bearbeitet durch User
von Zeno (Gast)


Lesenswert?

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.

von Zeno (Gast)


Lesenswert?

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.

von Mikro 7. (mikro77)


Lesenswert?

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
bool inRange(int a,int b,int c)
2
{
3
  if (b >= 0)
4
  {
5
    // min check for c in [a,...]
6
    if (c < a) return false ;
7
    
8
    // c is always in [a,>INT_MAX]
9
    if (a > INT_MAX-b) return true ;
10
    
11
    // c in [a,a+b] w/o overflow (UB)
12
    return c <= a+b ; 
13
  }
14
  else
15
  {
16
    // max check for c in [...,a]
17
    if (a < c) return false ;
18
    
19
    // c is always in [<INT_MIN,a]
20
    if (a < INT_MIN-b) return true ;
21
22
    // [a+b,a] w/o underflow (UB)
23
    return a+b <= c ;
24
  }
25
}

Hier nochmal Wilhelms Lösung 
(Beitrag "Re: C: Prüfen, ob Variable in Intervall"):
1
bool in_range (int16_t c, int16_t a, int16_t b)
2
{
3
    uint16_t c_a = (uint16_t) c - (uint16_t) a;
4
5
    if (b >= 0 && c >= a)
6
        return c_a <= (uint16_t) b;
7
8
    if (b <= 0 && c <= a)
9
        return -c_a <= - (uint16_t) b;
10
    
11
    return false;
12
}

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
bool inRange(int a,int b,int c)
3
{
4
  if (b >= 0)
5
  {
6
    // [a,...]
7
    if (c < a) return false ;
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) return false ;
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:
1
bool inRange(int a, int b, int c)
2
{
3
  bool c1 = (b >= 0) ;
4
  bool c2 = (a <= c) ;
5
  bool c3 = ((unsigned)c-(unsigned)a <= (unsigned)b) ;
6
  return
7
    ( c1 &  c2 &  c3) |
8
    (!c1 & !c2 & !c3) ;
9
}

von A. S. (Gast)


Lesenswert?

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)

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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
> bool inRange(int a,int b,int c)
2
> {
3
>   if (b >= 0)
4
>   {
5
>     // min check for c in [a,...]
6
>     if (c < a) return false ;
7
> 
8
>     // c is always in [a,>INT_MAX]
9
>     if (a > INT_MAX-b) return true ;
10
> 
11
>     // c in [a,a+b] w/o overflow (UB)
12
>     return c <= a+b ;
13
>   }
14
>   else
15
>   {
16
>     // max check for c in [...,a]
17
>     if (a < c) return false;
18
> 
19
>     // c is always in [<INT_MIN,a]
20
>     if (a < INT_MIN-b) return true ;
21
> 
22
>     // [a+b,a] w/o underflow (UB)
23
>     return a+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 <typename T>
6
bool in_range (T c, T a, T b)
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
    using U = typename std::make_unsigned<T>::type;
11
12
    U c_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
        return c_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
    return false;
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.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

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
>> bool inRange(int a,int b,int c)
2
>> {
3
>>   if (b >= 0)
4
>>   {
5
>>     // min check for c in [a,...]
6
>>     if (c < a) return false ;
7
>>
8
>>     // c is always in [a,>INT_MAX]
9
>>     if (a > INT_MAX-b) return true ;
10
>>
11
>>     // c in [a,a+b] w/o overflow (UB)
12
>>     return c <= a+b ;
13
>>   }
14
>>   else
15
>>   {
16
>>     // max check for c in [...,a]
17
>>     if (a < c) return false;
18
>>
19
>>     // c is always in [<INT_MIN,a]
20
>>     if (a < INT_MIN-b) return true ;
21
>>
22
>>     // [a+b,a] w/o underflow (UB)
23
>>     return a+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 <typename T>
6
> bool in_range (T c, T a, T b)
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
>     using U = typename std::make_unsigned<T>::type;
11
> 
12
>     U c_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
>         return c_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
>     return false;
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.

von A. S. (Gast)


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch User
von Heiko L. (zer0)


Lesenswert?

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?

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von x^2 (Gast)


Lesenswert?

1
 bool inRange(int a,int b,int c)
2
 {
3
   if (b >= 0)
4
   {
5
     // min check for c in [a,...]
6
     if (c < a) return false ;
7
 
8
     // c is always in [a,>INT_MAX]
9
     if (a > INT_MAX-b) return true ;
10
 
11
     // c in [a,a+b] w/o overflow (UB)
12
     return c <= a+b ;
13
   }
14
   else
15
   {
16
     // max check for c in [...,a]
17
     if (a < c) return false ;
18
 
19
     // c is always in [<INT_MIN,a]
20
     if (a < INT_MIN-b) return true ;
21
 
22
     // [a+b,a] w/o underflow (UB)
23
     return a+b <= c ;
24
   }
25
 }

Grob überflogen so ziemlich die einzige korrekte Funktion hier. Dies 
schließt den C++ Code mit ein. Danke dem Autor zur Ehrenrettung dieses 
Forums.

von Rainer V. (a_zip)


Lesenswert?

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

von zitter_ned_aso (Gast)


Lesenswert?

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

von Walter T. (nicolas)


Lesenswert?

zitter_ned_aso schrieb:
> Und einfacher.

Funktioniert aber nur, wenn die Intervallbreite durch zwei teilbar ist.

von Rainer V. (a_zip)


Lesenswert?

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

von Rainer V. (a_zip)


Lesenswert?

Walter T. schrieb:
> Funktioniert aber nur, wenn die Intervallbreite durch zwei teilbar ist.

PS. warum das denn?!

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.