Forum: Mikrocontroller und Digitale Elektronik Kehrwert Integer-Division


von Walter T. (nicolas)


Lesenswert?

Hallo zusammen,

ich habe mal wieder ein Integer-Denkproblem. Wie immer wäre mir eine 
Quelle (Link/Buchnennung) sogar noch lieber als die direkte Lösung.

Ich habe eine Funktion, die eine zur Compile-Zeit bekannte Konstante 
durch eine Variable dividiert:
1
#define MARKER 16777213ULL
2
3
4
uint32_t overflowVal(uint32_t divisor)
5
{
6
    uint64_t marker = MARKER;
7
    uint64_t overflow = marker * marker/(2*divisor);
8
    const uint64_t threshold = INT32_MAX/2;
9
10
    assert( overflow <= threshold ); 
11
12
    return overflow;
13
}

Für den Divisor gibt es einen kleinstmöglichen erlaubten Wert (>0), den 
ich exakt berechnen will. Dass es toll ist, wenn das zur Compilezeit 
geschieht, ist jetzt erst einmal nur am Rande interessant.

Der naive Ansatz:
1
uint32_t minDivider(void)
2
{
3
    uint64_t marker = MARKER;
4
    const uint64_t threshold = INT32_MAX/2;
5
6
    uint32_t dMin = marker*marker/(2*threshold);
7
    return dMin;
8
}
hat einen Eins-daneben-Fehler für alle bisher getesteten Werte von 
"Marker".

Wie kann ich den kleinsten Divisor d, bei dem d-1 einen unerlaubten 
Überlauf liefert und d im gültigen Bereich liegt, für beliebige Werte 
des Dividenden berechnen?

Viele Grüße
W.T.

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


Lesenswert?

Walter T. schrieb:
> ich habe mal wieder ein Integer-Denkproblem.

Hä?

Bahnhof meinerseits...

Also ich interpretiere das mal so:

Du hast

int64 i, j;

und willst feststellen, ab wann

erg = (i*i) / (2*j);

größer ist als 0x7FFFFFFF. Ist das so?

Also erstens wird das Ganze unterschiedlich, je nachdem in welcher 
Reihenfolge du die Rechenoperationen ausführst, weil du ja zum Erhalt 
möglichst vieler gültiger Stellen weder bei i*i einen Überlauf, noch bei 
i/(2*j) oder (i*i)/(2*j) einen Überlauf oder einen Unterlauf, sprich ne 
Null, haben willst.

Aber wenn du eine 64 Bit Zahl i durch eine Zahl j teilen willst, ohne 
daß das Ergebnis größer ist als 0x7FFFFFFF, dann mußt du den Bitabstand 
der führenden Bits beider Zahlen vergleichen und anschließend ggf. noch 
die Quasi-Mantissen.

Also mal ganz grob aus dem Handgelenk:
n = 0;
while ((i > j)
{ j = j<<1);
  ++n;
}
if (n > 30) //oder 31 ??
{ throwevent("Überlauf"); }

W.S.

von Joe F. (easylife)


Lesenswert?

Durch Umformen deiner Gleichung komme ich auf

divisor >= (marker * marker) / INT32_MAX
1
overflow <= threshold : (marker * marker) / (2 * divisor)     <= INT32_MAX/2
2
*2                    : (2 * marker * marker) / (2 * divisor) <= INT32_MAX
3
Bruch kürzen          : (marker * marker) / divisor           <= INT32_MAX
4
*divisor, /INT32_MAX  : (marker * marker) / INT32_MAX         <= divisor

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


Lesenswert?

Joe F. schrieb:
> Durch Umformen deiner Gleichung komme ich auf
>
> *2

Falls INT32_MAX wie üblich ungerade ist, dann ist (INT32_MAX/2)*2 != 
INT32_MAX.

Stattdessen: (INT32_MAX/2)*2 = INT32_MAX - 1 = INT32_MAX - (INT32_MAX % 
2)

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.