2013-04-02 36 views
7

Arrotondare il risultato della divisione al numero intero più vicino è pretty simple. Ma sto cercando di arrotondare un risultato della divisione in modo che l'operazione successiva dia la migliore approssimazione. Che è meglio spiegata con una semplice funzione:Round y = x * x al più vicino

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. 

posso arrotondare x_div al più vicino con l'aggiunta di 1<<(halfbits-1). Ma poiché x² non è una funzione lineare, y in generale non è arrotondato correttamente. Esiste un modo semplice e più accurato per calcolare (x*x) >> (halfbits*2) senza utilizzare tipi più grandi?

Penso che l'aggiunta di 3<<(halfbits-3) a x_div migliora l'arrotondamento, ma non può dimostrare che sia la soluzione migliore. Inoltre, può essere generalizzato per xⁿ?

Modifica: a grande richiesta mi prendo la libertà di "tradurre" la domanda in termini aritmetici puri (nessuna di queste cose che spostano il bit C ...).
Nota: tutte le divisioni successive sono le divisioni di interi, ad esempio 13/3 sarebbe 4.
Problema: non possiamo calcolare x^2 perché x è grande, quindi vorremmo calcolare (x^2)/(2^N).
A questo scopo si calcola
x_div = x/sqrt (2^N)
che poi quadrato:
y = x_div * x_div

Tuttavia, questo risultato è tipicamente breve del valore esatto di (x^2)/(2^N) e l'OP suggerisce di aggiungere 0,5 * sqrt (2^N) o forse 0,375 * sqrt (2^N) per approssimare meglio il risultato ...

come risposta Oli Charlesworth come suggerisce c'è un modo molto migliore per arrivare al valore effettivo, pensando a x^2 come (x_hi + x_lo)^2.

+7

Non sono perso con le operazioni di bit, ma potresti forse riformulare le operazioni in modo matematico e meno c centric? Non sto cercando di essere pedante, penso solo che contribuirebbe a chiarire il problema a portata di mano e aiutare a dichiarare il tuo obiettivo. –

+0

Diverse cose da provare: 'x_div_down * x_div_up',' x_div_near * x_div_near'. Se sei disposto a passare il tempo, potresti calcolare '(x_div * x_rest) >> (halfbit-1)' e correggere il risultato. –

+0

Quindi, per essere chiari, impliciti dal tuo '(x * x) >> (halfbits * 2)', in realtà vuoi un risultato accurato a bit * non arrotondato * di quello? O vuoi che il risultato matematico sia arrotondato correttamente? – hyde

risposta

8

Considerando che con il troncamento x_div causerà un errore di grandezza al massimo 1, con x_div*x_div l'errore potrebbe essere fino a 1<<(half_digits+2).

capire perché, consideriamo che possiamo esprimere questa quadratura (usando long multplication) come segue:

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

dove x_lo e x_hi sono le metà inferiore e superiore x, rispettivamente. Con qualche bella arte ASCII, possiamo considerare come questi tutti in fila:

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

Come possiamo vedere, i termini di ordine inferiore colpiscono più bit nel risultato finale.

Tuttavia, ognuno di questi termini deve rientrare nel tipo originale senza overflow. Quindi, con il cambio e il mascheramento appropriati, possiamo ottenere il risultato desiderato.

Naturalmente, il compilatore/hardware fa tutto questo per te se usi un tipo più grande, quindi dovresti farlo solo se ne hai l'opzione.

+0

Questo è ciò a cui stavo mirando ... tuttavia mi stavo improvvisamente chiedendo: non sappiamo se 'x * x' si adatta a' unsigned', e non mi è chiaro quale arrotondamento dovrebbe essere applicato al risultato ... –

+0

@ MatthieuM .: Non importa però. Ci interessa solo '(x * x) >> (half_bits * 2)'. Con il cambio appropriato, possiamo usare i termini sopra riportati per ottenere il risultato corretto. –

+0

Usando i bit 'n',' x_div' avrà i bit 'n/2'. Il valore massimo memorizzato in bit 'n' è' 2^n - 1'. Poiché '(2^n -1) - (2^(n/2) -1) * (2^(n/2) -1)' è sempre maggiore di zero, sappiamo che 'x_div * x_div' non sarà mai overflow 'n' bit. –

-4

Utilizzare TYPE int per "y". Immagino che risolverebbe lo scopo.

+0

Nota: non c'è niente di sbagliato nell'eliminare le risposte sbagliate in SO, e ottenere 4 downvote rapidamente è generalmente una buona indicazione che la risposta è sbagliata, anche se nessuno dei downvoters si è preso la briga di lasciare una spiegazione. – hyde

+0

@hyde: Beh, una spiegazione è che questo sicuramente non "risolve lo scopo";) –

+0

@OliCharlesworth sì, sicuramente non intendevo che i downvotes fossero sbagliati. Sono solo nel campo che preferisce lasciare commenti quando si fa downvoting, anche se è solo "la tua risposta è ... sbagliata". – hyde