2010-02-12 12 views
5

Sto appena iniziando a imparare l'assemblaggio nella mia classe di informatica e ho un compito di arrotondare un valore in virgola mobile utilizzando una modalità di arrotondamento specificata . Ho provato a implementarlo utilizzando fstcw, fldcw e frndint. Modificare i bit di controllo dell'arrotondamento, arrotondare il numero e ripristinare i bit di controllo precedenti (requisito per l'assegnazione).Utilizzo di numeri a precisione doppia nel montaggio in linea (GCC, IA-32)

L'attuale problema notevole è che l'istruzione fld %1 sembra caricare il valore errato nel registro st(0) virgola mobile (ad esempio, se chiamo la funzione con un valore di 2,6207, il numero -1,9427 (...) e-29 viene caricato nel registro). Ciò potrebbe essere dovuto a un uso improprio di asm() in linea gcc o altro, ma non sono sicuro del motivo.

Ecco quello che ho:

double roundD (double n, RoundingMode roundingMode) 
{ 
    // control word storage (2 bytes for previous, 2 for current) 
    char *cw = malloc(4*sizeof(char)); 
    char *cw2 = cw + 2; 

    asm("fstcw %3;" // store control word in cw 
     "mov %3,%4;" // copy control word into cw2 
     "and $0xF3FF,%4;" // zero out rounding control bits 
     "or %2,%4;" // put new mode into rounding control bits 
     "fldcw %5;" // load the modified control word 
     "fld %1;" // load n into st(0) 
     "frndint;" // round n 
     "fstp %0;" // load st(0) back into n 
     "fldcw %3;" // load the old control word from cw 
     : "=m" (n) 
     : "m" (n), "m" (roundingMode), 
      "m" (cw), "r" (cw2), "m" (cw2) // mov requires one argument in a register 
     ); 

    free(cw); 

    return n; 
} 

Apprezzerei tutto puntatori a cosa c'è di sbagliato in questo codice, in particolare in relazione alla linea fld %1 ei asm ingressi/uscite. (Naturalmente, se riesci a trovare altri problemi, sentiti libero di farmi sapere anche su di loro.) Non voglio che nessuno faccia i miei compiti per me, semplicemente indicami la giusta direzione. Grazie!

+0

Wow, è abbastanza cazzuto. Vorrei poterti aiutare, ma non posso! :) –

+0

puoi mostrarci l'output di assemblaggio di questa funzione? e il codice byte anche se non è troppo disturbo. –

+0

@John: il codice che verrà assemblato non è una sorpresa; è tutto in un blocco opaco gigante (a gcc) asm. :-P Considerando che dividerlo in molte dichiarazioni su asm (come il mio post) darebbe a gcc più spazio per fare qualcosa di diverso. –

risposta

2

Almeno un problema con il codice corrente è che utilizza le versioni a virgola mobile di precisione singola di fld e fstp. Se li sostituisci con fldl e fstpl, probabilmente funzionerà.

+0

L'ho cercato. Questa sembra essere la soluzione giusta perché gcc usa le istruzioni AT & T e richiede l'operando l, quindi FLDL ha ragione. –

+0

+1 Sì, sembra che le versioni * l funzioneranno. Il frammento che ho postato, quando è stato smontato, ha anche il suffisso * l. (Ovviamente, nel mio caso ho solo scelto di lasciar fare a gcc tutto il duro lavoro, usando i vincoli piuttosto che la codifica manuale nelle istruzioni load/store.) –

+0

Ho cercato di mantenere lo spirito della domanda ma la tua risposta è stata di gran lunga migliore:) – tyranid

2

Ecco cosa ho. Non è testato, ma si spera che sia meno complicato con cui lavorare. :-)

double 
roundd(double n, short mode) 
{ 
    short cw, newcw; 

    __asm__("fstcw %w0" : "=m" (cw)); 
    newcw = cw & 0xf3ff | mode; 
    __asm__("fldcw %w0" : : "m" (newcw)); 
    __asm__("frndint" : "+t" (n)); 
    __asm__("fldcw %w0" : : "m" (cw)); 
    return n; 
} 

Anche se, se non ti viene richiesto di utilizzare assembly per raggiungere il vostro modalità di arrotondamento, pensare di utilizzare le funzioni di <fenv.h> invece. :-)

+0

Sono obbligato a utilizzare il gruppo :) – jtbandes

+0

@jtbandes: Cool. In tal caso, sentiti libero di testare la mia versione e fammi sapere che cosa è necessario correggere. :-) –

+0

Come funziona il vincolo '+ t'? Non riesco a trovare informazioni su di esso dove ho trovato gli altri. – jtbandes

0

Come il segno cambia, significa che il bit di segno (che è il più significativo, il primo) non è corretto. Suppongo che il puntatore% 1 sia allineato erroneamente. Se hai un byte, può iniziare su 0,1,2 ... ma se accedi a due byte, l'indirizzo deve essere 0,2,4 .... e nel caso di doppio l'indirizzo deve essere pari divisibile per 8: 0,8,16

Quindi verificare se l'indirizzo che si utilizza per caricare il valore è divisibile per 8. Assemblaggio ha la parola chiave align per garantire che i dati siano allineati correttamente.

+1

Questo allineamento NON è necessario per x86 ma solo consigliabile per le prestazioni. –

Problemi correlati