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?