2010-02-12 26 views
5

Estoy empezando a aprender a ensamblar en mi clase de informática y tengo una tarea para redondear un valor de punto flotante usando un modo de redondeo especificado . Intenté implementar esto usando fstcw, fldcw y frndint. Modifico los bits de control de redondeo, redondeo el número y luego restauro los bits de control previos (un requisito de la asignación).Trabajando con números de precisión doble en ensamblado en línea (GCC, IA-32)

El problema actual pendiente es que la instrucción fld %1 parece cargar el valor incorrecto en el registro de coma flotante st(0) (por ejemplo, si llamo a la función con un valor de 2.6207, el número -1.9427 (...) e-29 se carga en el registro). Esto puede ser debido a un mal uso de gcc en línea asm(), o algo más, pero no estoy seguro de por qué sucede.

Esto es lo que tengo:

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; 
} 

lo agradecería cualquier punteros a lo que está mal con ese código, específicamente relacionado con la línea fld %1 y los asm entradas/salidas. (Por supuesto, si puede encontrar otros problemas, no dude en comunicármelos también). No quiero que nadie haga mi tarea, solo apúnteme en la dirección correcta. ¡Gracias!

+0

Guau, eso es bastante rudo. Ojalá pudiera ayudar, ¡pero no puedo! :) –

+0

¿Puede mostrarnos el resultado de ensamblaje de esta función? y los bytes de código también si no es demasiado problema. –

+0

@John: El código que se ensamblará no es sorprendente; todo está en un bloque asm gigante opaco (a gcc). :-P Mientras que dividirlo en muchas declaraciones de asm pequeñas (como mi publicación) le daría a gcc más latitud para hacer algo diferente. –

Respuesta

2

Al menos un problema con su código actual es que está utilizando las versiones de coma flotante de precisión simple de fld y fstp. Si los reemplaza con fldl y fstpl, probablemente funcione.

+0

Lo busqué. Esta parece ser la solución correcta porque gcc usa las instrucciones de AT & T y exige el operando l, por lo que FLDL tiene razón. –

+0

+1 Sí, parece que las * versiones funcionarán. El fragmento que publiqué, cuando se desensambló, también tiene el sufijo * l. (Obviamente, en mi caso, simplemente decidí dejar que gcc hiciera todo el trabajo duro, usando restricciones en lugar de codificar manualmente en las instrucciones de carga/almacenamiento). –

+0

Intenté mantener el espíritu de la pregunta pero tu respuesta fue mucho mejor:) – tyranid

2

Esto es lo que tengo. No está probado, pero espero que sea menos complicado para que trabajes. :-)

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; 
} 

Aunque, si usted no está obligado a usar el montaje para lograr su modo de redondeo, pensar en usar las funciones de <fenv.h> lugar. :-)

+0

Estoy obligado a usar el ensamblaje :) – jtbandes

+0

@jtbandes: Cool. En ese caso, siéntase libre de probar mi versión y dejarme saber lo que necesita reparación. :-) –

+0

¿Cómo funciona la restricción '+ t'? No puedo encontrar información al respecto donde encontré a los demás. – jtbandes

0

Cuando el signo cambia, significa que el bit de signo (que es el más significativo, el primero) no es correcto. Supongamos que el puntero% 1 está incorrectamente alineado. Si tiene un byte, puede comenzar en 0,1,2 ... pero si accede a dos bytes, la dirección debe ser 0,2,4 .... y en el caso de del doble de la dirección debe ser par divisible por 8: 0,8,16

Compruebe si la dirección que utiliza para cargar el valor es divisible entre 8. Assembly tiene la palabra clave align para garantizar que sus datos estén alineados correctamente.

+1

Esa alineación NO es necesaria para x86, pero solo es recomendable para el rendimiento. –

Cuestiones relacionadas