Forum: Mikrocontroller und Digitale Elektronik Multiplikation/Division


von Walter T. (nicolas)


Lesenswert?

Hallo zusammen,

ich bin wieder bei dem Versuch, eine Funktion "kugelsicher" zu bekommen. 
Naiv sieht die Funktion so aus:
1
    int32_t quotient_naive(int64_t cnt, int16_t z, int16_t n)
2
    {
3
        assert( z != 0 );
4
        assert( n > 0 );
5
        assert( cnt != INT64_MIN );
6
7
        /* Naiv: beruecksichtigt moeglichen Ueberlauf von cnt * 1000 oder cnt * 1000 * z nicht */
8
        int64_t res = cnt * 1000LL * z/n;
9
10
        /* Ergebnis saettigen auf int32. Die Symmetrie ist optional, es darf auch normal um
11
           INT32_MIN/INT_MAX geklemmt sein */
12
        if( res > INT32_MAX )
13
        {
14
            res = INT32_MAX;
15
        }
16
        else if( res < -INT32_MAX )
17
        {
18
            res = -INT32_MAX;
19
        }
20
21
        return res;
22
    }
Eine 64-Bit-Variable soll mit einer Variablen und einer Konstanten 
multipliziert und anschließend durch eine andere Variable dividiert 
werden. Das Ergebnis soll auf die Bitbreite des Rückgabewertes gesättigt 
werden, wobei es nicht von Belang ist, ob symmetrisch gesättigt wird, 
oder INT32_MIN mitgenommen wird.

Bis auf die drei Assertions, die ich hingeschrieben habe, gibt es keine 
Annahmen über den Wertebereich.

Mein erster Versuch, das Ganze "kugelsicher" zu bekommen, sieht so aus:
1
    int32_t quotient_1(int64_t cnt, int16_t z, int16_t n)
2
    {
3
        assert( z != 0 );
4
        assert( n > 0 );
5
        assert( cnt != INT64_MIN );
6
7
        if( ABS(cnt) >= INT32_MAX * n )
8
        {
9
            return SIGN(cnt) * INT32_MAX;
10
        }
11
12
        assert( ABS(cnt) < INT32_MAX * INT16_MAX );
13
14
        cnt *= z;
15
16
        if( ABS(cnt) >= INT32_MAX * n )
17
        {
18
            return SIGN(cnt) * INT32_MAX;
19
        }
20
21
        cnt *= 1000LL;
22
        cnt /= n;
23
24
        if( ABS(cnt) >= INT32_MAX )
25
        {
26
            return SIGN(cnt) * INT32_MAX;
27
        }
28
        else
29
        {
30
            return cnt;
31
        }
32
    }

 a) Habe ich etwas übersehen?
 b) Geht es besser? Wenn ja: Wie?

: Bearbeitet durch User
von Theor (Gast)


Lesenswert?

Vielleicht sollte man ja lieber "manngedeckte" Funktionen schreiben. 
Oder "fischige Werte" mit "Bitnetzen fangen". Man könnte INT_MAX auch 
auf der "Kurveninnenseite überholen".

Sehr hübsch, - und mir liegen sie mehr -, sind auch Metaphern aus der 
Küche. Wie wärs mit "Zahlenschaum abschöpfen"?

von Nils (Gast)


Lesenswert?

Magst Du ins einen Tip geben für welche Platform das sein soll, und 
welchen Compiler du benutzt?

Mit Compiler Extensions kann man da noch ein bischen zaubern.


Und: Ist der Faktor 1000 vorgegeben, oder kann da auch eine Power-of-two 
wie 1024 oder 65536 rein? Das macht u.U. die Berechnung etwas einfacher.

von Walter T. (nicolas)


Lesenswert?

Nils schrieb:
> Magst Du ins einen Tip geben für welche Platform das sein soll, und
> welchen Compiler du benutzt?

Warum? int64 ist auf allen Plattformen, die das unterstützen, gleich, 
und int128 wird auf keiner mir bekannten Plattform unterstützt. Mit den



Nils schrieb:
> Und: Ist der Faktor 1000 vorgegeben, oder kann da auch eine Power-of-two
> wie 1024 oder 65536 rein?

Nein, dann wäre das ja einfach.

Im Prinzip läuft es ja darauf hinaus, den Vergleich a * b > c ? 
auszuführen, ohne die Multiplikation a * b auszuführen, weil diese 
überlaufen könnte. Wahrscheinlich gibt es ein Idiom

  (a * b > c) == a > (c + d)/b

Das würde die Sache vereinfachen. Aber ich knobele noch am d.



Nils schrieb:
> Magst Du ins einen Tip geben für welche Platform das sein soll, und
> welchen Compiler du benutzt?

Wenn Du darauf anspielst, ob __builtin_umull_overflow() zur Verfügung 
steht: Ja, tut es. Aber ich bin mir momentan nicht sicher, ob das die 
Sache vereinfacht.

: Bearbeitet durch User
von Theor (Gast)


Lesenswert?

Walter T. schrieb:
> Nils schrieb:
>> Und: Ist der Faktor 1000 vorgegeben, oder kann da auch eine Power-of-two
>> wie 1024 oder 65536 rein?
>
> Nein, dann wäre das ja einfach.
>
> Im Prinzip läuft es ja darauf hinaus, den Vergleich a * b > c ?
> auszuführen, ohne die Multiplikation a * b auszuführen, weil diese
> überlaufen könnte. Wahrscheinlich gibt es ein Idiom
>
>   (a * b > c) == a > (c + d)/b
>
> Das würde die Sache vereinfachen. Aber ich knobele noch am d.


Aber wieso taucht da plätzlich d auf?

(a * b > c) == (a > c/b). Was Du vermutlich aber weißt.

Magst Du das 'd' mal erklären?

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.