In che modo la matematica fondamentale viene valutata in modo efficiente dai linguaggi di programmazione?


22

Mentre sono sempre più coinvolto nella teoria alla base della programmazione, mi trovo affascinato e sbalordito da cose apparentemente semplici. Mi rendo conto che la mia comprensione della maggior parte dei processi fondamentali è giustificata dalla logica circolare

D : Come funziona?

A : Perché lo fa!

Odio questa realizzazione! Adoro la conoscenza e, soprattutto, adoro imparare, il che mi porta alla mia domanda (sebbene sia ampia).

Domanda:

Come vengono valutati gli operatori matematici fondamentali con i linguaggi di programmazione?

Come sono stati migliorati i metodi attuali?

Esempio

var = 5 * 5; 

La mia interpretazione:

$num1 = 5; $num2 = 5; $num3 = 0;
while ($num2 > 0) {
    $num3 = $num3 + $num1;
    $num2 = $num2 - 1;
}
echo $num3;

Questo sembra essere altamente inefficiente. Con fattori superiori, questo metodo è molto lento mentre il metodo standard incorporato è istantaneo. Come simuleresti la moltiplicazione senza iterare l'aggiunta?

var = 5 / 5;

Come viene fatto? Non riesco a pensare a un modo per dividerlo letteralmente 5 in 5 parti uguali.

var = 5 ^ 5; 

Iterazioni di iterazioni di addizione? La mia interpretazione:

$base = 5;
$mod = 5;
$num1 = $base;
while ($mod > 1) {

    $num2 = 5; $num3 = 0;
    while ($num2 > 0) {
        $num3 = $num3 + $num1;
        $num2 = $num2 - 1;
    }
    $num1 = $num3;
    $mod -=1;
}
echo $num3;

Ancora una volta, questo è ESTREMAMENTE inefficiente, ma non riesco a pensare a un altro modo per farlo. Questa stessa domanda si estende a tutte le funzioni matematiche correlate gestite automagicamente.


1
Un po 'di retroscena su di me, sto andando al college per l'informatica, e più tardi nella vita teoria teorica e forse anche filosofia e fisica teorica. Molte aspirazioni, poco tempo.
Korvin Szanto,

10
È sicuro presumere di aver visto tutti i collegamenti da en.wikipedia.org/wiki/Category:Computer_arithmetic ?
JB King,

2
Fondamentalmente, è simile a come ti è stato insegnato a fare moltiplicazioni a più cifre e una lunga divisione nella scuola elementare. Prendi una cifra di A, moltiplica per B. Sposta per dieci. Prendi la cifra successiva di A, moltiplica per B. Ripeti per tutte le cifre, aggiungi tutto insieme. Poiché è binario, la moltiplicazione a singola cifra è più semplice (è x0 o x1) e invece di spostarsi di dieci, raddoppi. La divisione è simile.
Chiedi a Monica il

Risposte:


35

Per capire davvero come funziona l'aritmetica all'interno di un computer è necessario aver programmato in linguaggio assembly. Preferibilmente uno con una parola piccola e senza istruzioni di moltiplicazione e divisione. Qualcosa come il 6502.

Sul 6502, praticamente tutta l'aritmetica viene eseguita in un registro chiamato Accumulatore. (Un registro è una posizione di memoria speciale all'interno del processore a cui è possibile accedere rapidamente.) Quindi per aggiungere due numeri, si carica il primo numero nell'accumulatore, quindi si aggiunge il secondo numero ad esso.

Ma è semplicissimo. Poiché il 6502 è un processore a 8 bit, può gestire solo numeri da 0 a 255. Il più delle volte vorrai poter lavorare con numeri più grandi. Devi aggiungerli a pezzi, 8 bit alla volta. Il processore ha un flag Carry impostato quando il risultato dell'aggiunta di due numeri trabocca sull'accumulatore. Il processore lo aggiunge quando fa un'aggiunta, quindi può essere usato per "portare l'1" supponendo che inizi con il byte di ordine più basso di un numero. Un'aggiunta multibyte sul 6502 è simile alla seguente:

  1. Cancella flag carry (CLC)
  2. Caricare il byte di ordine più basso del primo numero (LDA, accumulatore di carico)
  3. Aggiungi byte di ordine inferiore del secondo numero (ADC, aggiungi con carry)
  4. Memorizza il byte di ordine più basso del risultato (STA, store accumulator)
  5. Ripetere i passaggi da 2 a 4 con byte di ordine superiore successivi
  6. Se alla fine, il carry è impostato, hai traboccato; intraprendere le azioni appropriate, come la generazione di un messaggio di errore (BCS / BCC, branch se carry set / clear)

La sottrazione è simile se non si imposta prima il carry, si usa l'istruzione SBC invece di ADC e alla fine il carry è chiaro se c'era underflow.

Ma aspetta! E i numeri negativi? Bene, con il 6502 questi sono memorizzati in un formato chiamato complemento a due. Supponendo un numero a 8 bit, -1 viene memorizzato come 255, perché quando aggiungi 255 a qualcosa, ne ottieni uno in meno nell'accumulatore (più un carry). -2 viene memorizzato come 254 e così via, fino a -128, che viene memorizzato come 128. Quindi per gli interi con segno, metà dell'intervallo 0-255 di un byte viene utilizzata per numeri positivi e metà per numeri negativi. (Questa convenzione ti consente di controllare solo il bit più alto di un numero per vedere se è negativo.)

Pensalo come un orologio di 24 ore: l'aggiunta di 23 all'ora comporterà un'ora prima (il giorno successivo). Quindi 23 è l'equivalente modulare dell'orologio a -1.

Quando si utilizza più di 1 byte, è necessario utilizzare numeri più grandi per i negativi. Ad esempio, i numeri interi a 16 bit hanno un intervallo compreso tra 0 e 65536. Quindi 65535 è usato per rappresentare -1, e così via, perché l'aggiunta di 65535 a qualsiasi numero si traduce in uno in meno (più un carry).

Sul 6502 ci sono solo quattro operazioni aritmetiche: aggiungere, sottrarre, moltiplicare per due (spostamento a sinistra) e dividere per due (spostamento a destra). La moltiplicazione e la divisione possono essere fatte usando solo queste operazioni quando si fa trading in binario. Ad esempio, considera di moltiplicare 5 (binario 101) e 3 (binario 11). Come per la moltiplicazione decimale lunga, iniziamo con la cifra destra del moltiplicatore e moltiplichiamo 101 per 1, dando 101. Quindi spostiamo il moltiplicatore a sinistra e moltiplichiamo 1010 per 1, dando 1010. Quindi sommiamo questi risultati insieme, dando 1111, o 15. Dato che moltiplichiamo sempre solo per 1 o 0, non moltiplichiamo realmente; ogni bit del moltiplicatore funge semplicemente da flag che ci dice se aggiungere o meno il multiplicando (spostato).

La divisione è analoga alla divisione lunga manuale utilizzando i divisori di prova, tranne che in binario. Se si sta dividendo per una costante, è possibile farlo in modo analogo alla sottrazione: anziché dividere per X, si moltiplica per una resa precalcolata di 1 / X che produce il risultato desiderato più un overflow. Ancora oggi, questo è più veloce della divisione.

Ora prova a eseguire calcoli in virgola mobile in assembly o convertire i numeri in virgola mobile in formati di output gradevoli in assembly. E ricorda, è il 1979 e la velocità di clock è di 1 MHz, quindi devi farlo nel modo più efficiente possibile.

Le cose funzionano ancora in questo modo oggi, tranne che con parole più grandi e più registri, e ovviamente la maggior parte della matematica è fatta ora dall'hardware. Ma è ancora fatto nello stesso modo fondamentale. Se si sommano il numero di turni e quelli richiesti per un moltiplicare, ad esempio, si correla piuttosto bene al numero di cicli richiesti per un'istruzione di moltiplicazione hardware su processori precoci con tale istruzione, come il 6809, dove è stata eseguita nel microcodice più o meno allo stesso modo in cui lo faresti manualmente. (Se si dispone di un budget di transistor più grande, esistono modi più rapidi per eseguire i turni e gli aggiunte, quindi i processori moderni non eseguono queste operazioni in sequenza e possono eseguire moltiplicazioni in un solo ciclo.)


3
Ehi, grazie per la tua spiegazione molto dettagliata! È esattamente quello che volevo! Essendo al mio livello, spesso dimentichi che ciò che ti supporta è generalmente più complesso di qualsiasi cosa tu stia facendo. Questo è il motivo esatto per cui voglio studiare informatica. Odio il fatto che se dovessi tornare indietro nel tempo, non saprei nulla che cambi il mondo, solo come formulare un'istruzione SQL corretta;) Ad ogni modo, grazie mille per aver dedicato del tempo a scrivere questa risposta, mi hai dato un tester del gusto in quello che sto per approfondire.
Korvin Szanto,

7
non sono d'accordo, l'assemblaggio è ancora troppo elevato, se vuoi sapere come i computer fanno l'aritmetica devi guardare l'hardware, o almeno gli algoritmi hardware
jk.

Eh. Una volta che sai che ci sono additivi e cambi, è facile immaginarli controllati dall'hardware che dal software ed è più facile giocare con il software.
kindall

4
-1. La moltiplicazione dell'hardware non è stata eseguita con turni e si aggiunge da quasi 3 decenni ormai e molte CPU possono fare una moltiplicazione in un ciclo. Controlla l'articolo di Wikipedia su Binary Multiplierper i dettagli.
Mason Wheeler,

24

Alla fine, le operazioni aritmetiche di base vengono eseguite nell'hardware. Più precisamente, nella CPU (o effettivamente, una sua sottoparte)

In altre parole, sono circuiti elettronici. Imposta i bit appropriati come input e otterrai i bit appropriati come output. È una combinazione di porte logiche di base.

http://en.wikipedia.org/wiki/Adder_%28electronics%29

http://en.wikipedia.org/wiki/Binary_multiplier


3
Gli algoritmi per l'hardware sono accuratamente specificati e possono essere studiati separatamente dall'hardware.
S.Lott

@ S.Lott: trovo il tuo commento confuso. Per me, gli algoritmi implicano una serie di passaggi che segui, una procedura, qualcosa che puoi programmare. Qui, stiamo parlando di circuiti elettronici che eseguono le operazioni aritmetiche di base. In altre parole, solo una sequenza di porte in cui scorre la corrente. Quindi è più "logico" che "algoritmico". ... i miei 2 centesimi.
Dagnelies,

6
Un algoritmo è "finito, definito ed efficace". Può essere in circuiti, o fatto con carta e matita, o fatto con Tinkertoy o molecole in un piatto o DNA. L'algoritmo può essere qualsiasi cosa. Un circuito elettronico deve seguire un algoritmo definito. Non supera magicamente la necessità di algoritmi.
S.Lott

1
Un processo che consiste in un solo passaggio sarebbe considerato un "algoritmo"? FWIW, i circuiti elettronici generalmente seguono una tabella di verità - una singola fase di elaborazione. Il fatto che la tabella della verità finisca per essere "compilata" in porte a più livelli non nega il fatto che si tratta di un processo a singolo passaggio.
Slebetman

2
@ S.Lott: Un primo commento più appropriato sarebbe: la "logica" dell'hardware è accuratamente specificata e può essere studiata separatamente dall'hardware. E infatti lo è. Lo studio della logica binaria si chiama algebra booleana.
Slebetman,

6

Tutto questo è coperto da una completa pazienza in The Art of Computer Programming di Don Knuth.

Algoritmi efficienti per aggiungere, sottrarre, moltiplicare e dividere sono tutti descritti in dettaglio completo.

Puoi leggere cose come questa che coprono bene la divisione.

http://research.microsoft.com/pubs/151917/divmodnote.pdf


5

Viene eseguito in picosecondi da circuiti elettronici. "Moltiplicatore hardware" di Google ecc. Per i dettagli. Le CPU moderne sono il risultato estremamente complesso di decenni di miglioramento continuo.

A proposito, dal momento che non si moltiplica per l'aggiunta ripetuta, perché immagini un computer?


La mia domanda riguarda il ragionamento dietro le funzioni piuttosto che le funzioni stesse, capisco che è interpretato dal processore, sono curioso di sapere come. In particolare la teoria alla base e come potrebbe essere replicata in pseudo-codice.
Korvin Szanto,

1
La moltiplicazione che faccio nella mia testa è la memoria. Anche una lunga moltiplicazione richiede l'iterazione nel modo in cui l'ho fatto. Continuerò e lancerò insieme una funzione per una lunga moltiplicazione
Korvin Szanto,

2
@Korvin, il libro che ho raccomandato ti servirà bene se questo è il tuo interesse. Consiglio anche "Struttura e interpretazione dei programmi per computer" di Harold Abelson e Gerald Jay Sussman. Si occupa di queste domande in modo approfondito.
Jonathan Henson,

Diversi primi computer supportavano solo addizioni e sottrazioni. Alcuni supportano solo la sottrazione! Quindi l'operazione x = y * z è stata implementata come do (z volte) {x + y} allo stesso modo la divisione x = y / z è stata implementata come while (y> z) {x + 1; y = y - z}
James Anderson,

@James: hanno supportato il turno? Mi aspetterei che la moltiplicazione fosse fatta tramite shift e add, mentre la divisione era shift, confronta, sottrai.
Kevin Cline,

4

Questo non vuole essere una risposta esaustiva, ma dovrebbe darti un'idea di come vengono implementate le cose. Come probabilmente saprai, i numeri sono rappresentati in binario. Ad esempio un computer potrebbe rappresentare il numero 5 come 00000101. Un'operazione molto semplice che un computer può fare è spostare a sinistra, il che darebbe 00001010 che è il decimale 10. Se fosse spostato a destra due volte sarebbe 00010100 (decimale 20). Ogni volta che spostiamo le cifre a sinistra di 1 volta raddoppiamo il numero. Supponiamo di avere un numero x e di volerlo moltiplicare per 17. Potrei spostare x a sinistra 4 volte e quindi aggiungere x al risultato (16x + x = 17x). Questo sarebbe un modo efficace per moltiplicare un numero per 17. Ciò dovrebbe darti un'idea di come un computer può moltiplicare numeri di grandi dimensioni senza semplicemente usare l'aggiunta ripetuta.

La divisione può usare combinazioni di addizione, sottrazione, spostamento a destra, spostamento a sinistra, ecc. Ci sono anche numerosi trucchi per elevare i numeri agli esponenti.


Per essere chiari, in genere puoi spostarti di più di un bit alla volta. Così quei 4 operazioni di scorrimento sono in realtà una sola operazione, come: shl r0, 4.
Caleb,

4

Quando ero un bambino, ho imparato a moltiplicare e dividere con una penna e un foglio, senza perdere tempo con troppe aggiunte. In seguito ho appreso che anche le radici quadrate sono calcolabili in questo modo.

All'università ho imparato a calcolare le operazioni trigonometriche e logaritmiche con una dozzina di moltiplicazioni, divisioni e aggiunte. Lo chiamavano serie Taylor.

Prima di allora, mio ​​padre mi aveva regalato un libro in cui quelle complesse operazioni erano già state calcolate per centinaia di valori e presentate in tabelle. C'erano anche alcune spiegazioni per stimare l'errore quando si voleva il seno di un valore tra due valori calcolati.

Unità intere, unità a virgola mobile, GPU e DSP implementano solo quelle vecchie tecniche sul silicio.


3

Cercherò di darti un'idea di come i circuiti digitali sono progettati per risolvere i problemi di elaborazione digitale utilizzando i problemi che poni: come le CPU implementano aggiunte e moltiplicazioni.

Innanzi tutto, lasciamo che la domanda diretta si allontani: in che modo un linguaggio di programmazione valuta in modo efficiente le moltiplicazioni e le aggiunte. La risposta è semplice, li compilano in moltiplicano e aggiungono istruzioni. Ad esempio, il seguente codice:

a = 1 + 1;
b = a * 20;

è semplicemente compilato per qualcosa come:

ADD 1 1  a
MUL a 20 b

(nota che il suddetto assembly è per una CPU immaginaria che non esiste, per semplicità).

A questo punto ti rendi conto che la risposta di cui sopra sposta semplicemente il problema e risolverlo con la magia dell'hardware. La domanda di follow-up è ovviamente come funziona quella magia hardware?

Vediamo prima il problema più semplice: aggiunta.

Per prima cosa facciamo un problema familiare, aggiungendo regolarmente numeri di base 10:

 17
+28

Il primo passo sarebbe quello di aggiungere 7 e 8. Ma questo risulta in 15 che è più di una singola cifra. Quindi portiamo il 1:

(1)
 17
+28
= 5

Ora aggiungiamo 1, 1 e 2 insieme:

 17
+28
=45

Quindi da questo otteniamo le seguenti regole:

  1. quando il risultato dell'addizione è più di una cifra, manteniamo la cifra meno significativa e portiamo avanti la cifra più significativa

  2. se abbiamo una cifra portata avanti nella nostra colonna, la aggiungiamo insieme ai numeri che stiamo aggiungendo

Ora è il momento di interpretare le regole sopra nella base 2 - algebra booleana.

Quindi, nell'algebra booleana, aggiungendo 0 e 1 insieme = 1. Aggiungendo 0 e 0 = 0. E aggiungendo 1 e 1 = 10 che è più di una cifra, quindi portiamo avanti 1.

Da questo possiamo costruire una tabella di verità:

a b  |  sum  carry
-------------------
0 0  |   0     0
0 1  |   1     0
1 0  |   1     0
1 1  |   0     1

Da questo, possiamo costruire due circuiti / equazioni booleane - uno per l'output della somma e uno per l'output di carry. Il modo più ingenuo è semplicemente elencare tutti gli input. Qualsiasi tabella di verità, non importa quanto grande e complessa possa essere riformulata in questa forma:

(AND inputs in first row) OR (AND of inputs in second row) OR ...

Questa è sostanzialmente la somma dei prodotti. Osserviamo solo gli output che generano un 1 e ignoriamo gli 0:

sum = (NOT a AND b) OR (a AND NOT b)

Sostituiamo AND OR e NOT con i simboli del linguaggio di programmazione per facilitare la lettura:

sum = (!a & b) | (a & !b)

Fondamentalmente, abbiamo convertito la tabella in questo modo:

a b  |  sum  equation
-------------------
0 0  |   0   
0 1  |   1   (!a & b)
1 0  |   1   (a & !b)
1 1  |   0   

Questo può essere implementato direttamente come un circuito:

                _____
 a ------------|     |
    \          | AND |-.     ____
     \  ,-NOT--|_____|  \   |    |
      \/                 `--| OR |----- sum
      /\        _____    ,--|____|
     /  `-NOT--|     |  /
    /          | AND |-`
 b ------------|_____|

I lettori osservanti noteranno a questo punto che la logica di cui sopra può effettivamente essere implementata come un singolo gate - un gate XOR che ha convenientemente il comportamento richiesto dalla nostra tabella di verità:

                _____
 a ------------|     |
               | XOR |---- sum
 b ------------|_____|

Ma se il tuo hardware non ti fornisce un gate XOR, i passaggi sopra indicati riguardano come definirlo e implementarlo in termini di porte AND, OR e NOT.

Il modo in cui dovresti convertire i gate logici in hardware reale dipende dall'hardware che hai. Possono essere implementati utilizzando vari meccanismi fisici purché il meccanismo fornisca una sorta di comportamento di commutazione. Le porte logiche sono state implementate con qualsiasi cosa, dai getti d'acqua o sbuffi d'aria (fluidica) ai transistor (elettronica) ai marmi che cadono. È un grande argomento a sé stante, quindi ho intenzione di sorvolarlo e dire che è possibile implementare le porte logiche come dispositivi fisici.

Ora facciamo lo stesso per il segnale carry. Poiché esiste una sola condizione in cui il segnale carry è vero, l'equazione è semplicemente:

carry = a & b

Quindi trasportare è semplice:

                _____
 a ------------|     |
               | AND |---- carry
 b ------------|_____|

Combinandoli insieme otteniamo quello che è noto come il mezzo sommatore:

                _____
 a ------;-----|     |
         |     | XOR |---- sum
 b --;---|-----|_____|
     |   |      _____
     |   '-----|     |
     |         | AND |---- carry
     '---------|_____|

Le equazioni per il circuito sopra riportato sembrano così:

sum = a ^ b
carry = a & b

Il mezzo sommatore manca qualcosa. Abbiamo implementato la prima regola - se il risultato è più di una cifra rispetto al riporto, ma non abbiamo implementato la seconda regola - se c'è un riporto aggiungilo insieme ai numeri.

Quindi per implementare un sommatore completo, un circuito di aggiunta che può aggiungere numeri che sono più di una cifra, dobbiamo definire una tabella di verità:

a b c  |  sum  carry
---------------------
0 0 0  |   0     0
0 0 1  |   1     0
0 1 0  |   1     0
0 1 1  |   0     1
1 0 0  |   1     0
1 0 1  |   0     1
1 1 0  |   0     1
1 1 1  |   1     1

L'equazione per la somma è ora:

sum = (!a & !b & c) | (!a & b & !c) | (a & !b & !c) | (a & b & c)

Possiamo passare attraverso lo stesso processo per scomporre e semplificare l'equazione e interpretarla come un circuito ecc. Come abbiamo fatto sopra, ma penso che questa risposta stia diventando eccessivamente lunga.

Ormai dovresti avere un'idea di come è progettata la logica digitale. Ci sono altri trucchi che non ho menzionato come le mappe di Karnaugh (utilizzate per semplificare le tabelle di verità) e i compilatori logici come l'espresso (in modo da non dover considerare le equazioni booleane a mano) ma la base è fondamentalmente ciò che ho descritto sopra:

  1. Decomponi il problema fino a quando non puoi lavorare a livello di singolo bit (cifra).

  2. Definire gli output desiderati utilizzando una tabella di verità.

  3. Converti la tabella in un'equazione booleana e semplifica l'equazione.

  4. Interpreta l'equazione come porte logiche.

  5. Converti il ​​tuo circuito logico in circuiti hardware reali implementando porte logiche.

Ecco come i problemi fondamentali (o meglio, di basso livello) sono davvero risolti: un sacco di tabelle di verità. Il vero lavoro creativo è la scomposizione di un'attività complessa come la decodifica MP3 a livello di bit in modo da poter lavorare su di essa con tabelle di verità.

Mi dispiace non ho il tempo di spiegare come implementare la moltiplicazione. Puoi provare a prenderlo in giro capendo le regole su quanto funziona la moltiplicazione, quindi interpretandolo in binario e poi provando a scomporlo in tabelle di verità. Oppure puoi leggere Wikipedia: http://en.wikipedia.org/wiki/Binary_multiplier


2

le istruzioni aritmetiche di base vengono eseguite con istruzioni di assemblaggio altamente efficienti.

Le istruzioni più complesse (o astratte) vengono eseguite in assembly con meccanismi di loop o vengono gestite in librerie standard.

Mentre studi matematica al college, inizierai a studiare cose come Lambda Calculus e la notazione Big-O. Tutti questi e molti altri sono usati dai programmatori per valutare e creare algoritmi efficienti. Ad ogni modo, le cose di base sono generalmente realizzate a basso livello come nell'assemblaggio o in c con puntatori.

Una grande introduzione a questo argomento è "Codice" di Charles Petzold.


1
O tabelle di ricerca. Molto più veloce per pre-calcolare i valori e cercarli. Esempi Sin / Cos / Tan (divisione intera sebbene questa sia pre-calcolata e memorizzata nell'hardware).
Martin York,

1

Prendi un libro come Fundamentals of Digital Logic ... , che penso sia ancora abbastanza standard per gli studenti di Freshman / Sophomore EE, e fatti strada (a cura di: costa una piccola fortuna, quindi cerca un'edizione usata o precedente di esso). Questo ti porterà attraverso gli additivi e i moltiplicatori e ti fornirà abbastanza background per iniziare a comprendere alcuni dei principi alla base di ciò che l'hardware sta facendo.

La tua risposta diventerà, a breve termine, "perché combina innumerevoli pezzi di logica più semplice per evocare questo comportamento complesso" invece di "perché lo fa".

Se vuoi provare a comprendere tutti i principi di come i programmi vengono compilati ed eseguiti, prova a farlo congiuntamente, in modo da poter finalmente vedere come tutto si incontra nel mezzo.


1

Ci sono molte buone risposte qui. Anche tu hai iniziato con l'idea giusta: operazioni complesse come la moltiplicazione sono costruite da operazioni più semplici. Come hai indovinato, ci sono modi più rapidi di moltiplicarsi senza un'istruzione di moltiplicazione rispetto all'uso di una serie di aggiunte. Qualsiasi moltiplicazione può essere implementata come la somma di moltiplicazioni minori o come una combinazione di turni e aggiunte. Esempi:

a = 5 + 5 + 5 + 5 + 5;           // 5*5, but takes 5 operations
b = (5 << 2) + 5;                // 5*5 in only 2 operations
c = (41 << 4) + (41 << 2) + 41   // 41*21 in 4 operations

Allo stesso modo la divisione può essere suddivisa in operazioni più piccole. XOR (^) è un'istruzione integrata su ogni processore che abbia mai visto, ma anche così può essere implementata come una combinazione di AND, OR e NOT.

Ho la sensazione, tuttavia, che risposte specifiche ti saranno meno soddisfacenti di un'idea generale del tipo di istruzioni fornite da un processore e di come tali istruzioni possano essere combinate in operazioni più complesse. Non c'è niente di meglio per quel tipo di curiosità di una sana dose di linguaggio assembleare. Ecco un'introduzione molto accessibile al linguaggio assembly MIPS.


1

Ecco come un moderno processore potrebbe implementare la moltiplicazione di due numeri interi a 64 bit:

Sai come fare una moltiplicazione a mano lunga. Per moltiplicare due numeri di 10 cifre, moltiplicare un numero di 10 cifre per ciascuna delle 10 cifre dell'altro numero, scrivere il risultato di 11 cifre uno sotto l'altro e spostato, quindi aggiungere tutto il numero.

Un moderno processore lo fa con tutti i 64 per 64 bit. Tuttavia, una moltiplicazione di due numeri a bit singolo è molto semplice: 1 x 1 = 1, tutti gli altri prodotti sono zero. Questo è implementato con un logico e. E a differenza del prodotto decimale, in cui il risultato può essere di due cifre, un prodotto binario di numeri a bit singolo è sempre un bit.

Quindi ora hai 64 righe di 64 bit che devono essere aggiunte. Ma 64 aggiunte di numeri a 64 bit sono slooooooow. Quindi il processore utilizza un sommatore 3/2 o un sommatore 7/3: se aggiungi 3 numeri a bit singolo, il risultato può essere 0, 1, 2 o 3, che si adatta a due bit. Se si aggiungono 7 numeri a bit singolo, il risultato è un numero compreso tra 0 e 7, che può essere rappresentato da 3 bit. IBM afferma di poter creare un sommatore 7/3 con solo 18 circuiti primitivi (documentazione PowerPC), scommetto che anche Intel e ARM possono farlo.

Hai 4096 bit, raggruppali in circa 600 gruppi di 7 bit nelle stesse posizioni di bit e usi circa 600 7/3 additivi per ridurre il risultato da 4096 bit a meno di 2.000. Quindi fai lo stesso ancora e ancora, fino a quando non finisci con coppie di bit che possono essere inserite in un normale sommatore completo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.