2013-04-02 18 views
7

Runden des Ergebnisses der Division auf die nächste ganze Zahl ist pretty simple. Aber ich versuche ein Ergebnis der Division zu runden, damit die nachfolgende Operation die beste Annäherung ergibt. Das ist am besten erklärt durch eine einfache Funktion:Rund y = x * x zum nächsten

const int halfbits = std::numeric_limits<unsigned int>::digits/2; 
unsigned x = foo(); // Likely big. 
unsigned x_div = x >> halfbits; // Rounded down 
unsigned y = x_div * x_div; // Will fit due to division. 

I x_div zum nächsten Runde kann 1<<(halfbits-1) durch Zugabe. Aber da x² keine lineare Funktion ist, wird y im Allgemeinen nicht richtig gerundet. Gibt es eine einfache und genauere Möglichkeit, (x*x) >> (halfbits*2) zu berechnen, ohne größere Typen zu verwenden?

Ich denke, dass das Hinzufügen von 3<<(halfbits-3) zu x_div die Rundung verbessert, kann aber nicht beweisen, dass das die beste Lösung ist. Kann dies auch für xⁿ verallgemeinert werden?

Bearbeiten: auf vielfachen Wunsch erlaube ich mir, die Frage in rein rechnerischen Begriffen "übersetzen" (keine dieser C Bit-Verschiebung Sache ...).
Hinweis: Alle Divisionen danach sind ganzzahlige Divisionen, zum Beispiel 13/3 wäre 4.
Problem: Wir können nicht x^2 berechnen, weil x groß ist, also möchten wir stattdessen berechnen (x^2)/(2^N).
Dazu berechnen wir
x_div = x/sqrt (2^N)
die wir dann Quadrat:
y = x_div * x_div

Dies ist jedoch aufgrund der Regel kurz vor dem exakten Wert von (x^2)/(2^N) und das OP schlägt vor, 0.5 * sqrt (2^N) oder vielleicht 0.375 * sqrt (2^N) hinzuzufügen, um das Ergebnis besser zu nähern ...

Oli Charlesworth 's Antwort schlägt vor, es gibt eine viel bessere Weise, zum zu gelangen tatsächlicher Wert, indem an x^2 als (x_hi + x_lo)^2 gedacht wird.

+7

Ich bin nicht mit den Bit-Ops verloren, aber könnten Sie vielleicht die Operationen in einer mathematischen zentrischen und weniger c-zentrischen Weise umformulieren? Ich versuche nicht, pedantisch zu sein, ich denke nur, dass es helfen würde, das vorliegende Problem zu klären und dabei zu helfen, Ihr Ziel zu formulieren. –

+0

Mehrere Dinge zu versuchen: 'x_div_down * x_div_up',' x_div_near * x_div_near'. Wenn Sie bereit sind, die Zeit zu verbringen, können Sie '(x_div * x_rest) >> (halfbit-1)' berechnen und das Ergebnis korrigieren. –

+0

Also, nur um klar zu sein, impliziert durch Ihre '(x * x) >> (halfbits * 2)', wollen Sie eigentlich * unrund * bitgenaues Ergebnis davon? Oder möchten Sie, dass das mathematische Ergebnis korrekt gerundet wird? – hyde

Antwort

8

Während mit x_div Kürzung eine Fehlergröße von höchstens 1 verursacht, mit x_div*x_div könnte der Fehler bis zu 1<<(half_digits+2) sein.

Um zu sehen, warum der Ansicht, dass wir diese Quadratur ausdrücken kann wie folgt (mit long multplication):

x * x = (x_lo + x_hi) * (x_lo + x_hi) 
     = x_lo^2 + x_hi^2 + 2*x_lo*x_hi 

wo x_lo und x_hi sind die unteren und oberen Hälften x sind. Mit einigen netten ASCII-Art, können wir überlegen, wie diese alle Line-Up:

MSB  :  :  :  LSB 
    +------+------+  :  : 
    | x_hi^2 |  :  : 
    +-----++-----++-----+:  : 
    :  | 2*x_lo*x_hi |:  : 
    :  +------++-----++------+ 
    :  :  | x_lo^2 | 
    :  :  +------+------+ 
    :<----------->:  :  : 
    Our result 

Wie wir sehen können, die niedriger Ordnung Bedingungen mehrere Bits in das Endergebnis beeinflussen.

Allerdings sollte jeder dieser Begriffe in den ursprünglichen Typ ohne Überlauf passen. Mit entsprechenden Verschiebungen und Maskierungen können wir das gewünschte Ergebnis erzielen.

Natürlich tut der Compiler/Hardware das alles für Sie, wenn Sie einen größeren Typ verwenden, also sollten Sie das tun, wenn Sie die Option haben.

+0

Darauf zielte ich ... aber ich fragte mich plötzlich: wir wissen nicht, ob 'x * x' in' unsigned' passt, und mir ist unklar, welche Rundung auf das Ergebnis angewendet werden soll ... –

+0

@ MatthieuM .: Es spielt aber keine Rolle. Wir interessieren uns nur für '(x * x) >> (half_bits * 2)'. Bei entsprechender Verschiebung können wir die obigen Ausdrücke verwenden, um das richtige Ergebnis zu erhalten. –

+0

Mit 'n' Bits wird' x_div' 'n/2' Bits haben. Der Maximalwert, der in "n" Bits gespeichert ist, ist "2^n - 1". Weil '(2^n -1) - (2^(n/2) -1) * (2^(n/2) -1)' immer größer als Null ist, wissen wir, dass 'x_div * x_div' niemals wird Überlauf 'n' Bits. –

-4

Verwenden Sie TYPE int für "y". Ich denke, das würde den Zweck lösen.

+0

Hinweis: Es ist nichts falsch daran, deine falschen Antworten bei SO zu löschen, und 4 schnelle Antworten zu erhalten, ist im Allgemeinen ein guter Hinweis darauf, dass deine Antwort falsch ist, auch wenn keiner der Downvoter eine Erklärung hinterlassen hat. – hyde

+0

@hyde: Nun, eine Erklärung ist, dass dies definitiv nicht "den Zweck zu lösen";) –

+0

@OliCharlesworth ja, ich habe definitiv nicht gemeint, Downvotes waren falsch. Ich bin gerade im Lager, das es vorzieht, Kommentare zu hinterlassen, auch wenn es nur "deine Antwort ist einfach ... falsch" ist. – hyde