2012-09-09 8 views
8

Ahora, aquí es la cabecera de la función de la función se supone que debo poner en práctica:¿Cómo realizar manualmente (en modo bit) (float) x?

/* 
* float_from_int - Return bit-level equivalent of expression (float) x 
* Result is returned as unsigned int, but 
* it is to be interpreted as the bit-level representation of a 
* single-precision floating point values. 
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while 
* Max ops: 30 
* Rating: 4 
*/ 
unsigned float_from_int(int x) { 
... 
} 

No se nos permite hacer operaciones de flotación, o cualquier tipo de fundición.

Ahora me trataron de poner en práctica el primer algoritmo dado en este sitio: http://locklessinc.com/articles/i2f/

Aquí está mi código:

unsigned float_from_int(int x) { 

// grab sign bit 

    int xIsNegative = 0; 
    int absValOfX = x; 

    if(x < 0){ 
    xIsNegative = 1; 
    absValOfX = -x; 
    } 




    // zero case 
    if(x == 0){ 
    return 0; 
    } 
    if(x == 0x80000000){ //Updated to add this 
    return 0xcf000000; 
    } 
    //int shiftsNeeded = 0; 

    /*while(){ 

    shiftsNeeded++; 
    }*/ 


    unsigned I2F_MAX_BITS = 15; 
    unsigned I2F_MAX_INPUT = ((1 << I2F_MAX_BITS) - 1); 
    unsigned I2F_SHIFT = (24 - I2F_MAX_BITS); 

    unsigned result, i, exponent, fraction; 

    if ((absValOfX & I2F_MAX_INPUT) == 0) 
    result = 0; 
    else { 
    exponent = 126 + I2F_MAX_BITS; 
    fraction = (absValOfX & I2F_MAX_INPUT) << I2F_SHIFT; 

    i = 0; 
    while(i < I2F_MAX_BITS) { 
     if (fraction & 0x800000) 
     break; 
     else { 
     fraction = fraction << 1; 
     exponent = exponent - 1; 
     } 
     i++; 
    } 
    result = (xIsNegative << 31) | exponent << 23 | (fraction & 0x7fffff); 
    } 
    return result; 
} 

Pero no funcionó (ver error en la prueba más adelante):

ERROR: Test float_from_int(8388608[0x800000]) failed... 
...Gives 0[0x0]. Should be 1258291200[0x4b000000] 

No sé a dónde ir desde aquí. ¿Cómo debo analizar el flotador desde este int?

editar # 1: Usted puede ser capaz de ver en mi código que también empecé a trabajar en este algoritmo (see this site):

I assumed 10-bit, 2’s complement, integers since the mantissa is only 9 bits, but the process generalizes to more bits.

Save the sign bit of the input and take the absolute value of the input. 
Shift the input left until the high order bit is set and count the number of shifts required. This forms the floating mantissa. 
Form the floating exponent by subtracting the number of shifts from step 2 from the constant 137 or (0h89-(#of shifts)). 
Assemble the float from the sign, mantissa, and exponent. 

embargo, que no parece correcto. ¿Cómo podría convertir 0x80000000? No tiene sentido.

editar # 2: Creo que es porque yo lo diga bits de Max es 15 ... hmmm ...

editar # 3: Tornillo que el viejo algoritmo, estoy empezando otra vez:

unsigned float_from_int(int x) { 

    // grab sign bit 

    int xIsNegative = 0; 
    int absValOfX = x; 

    if(x < 0){ 
    xIsNegative = 1; 
    absValOfX = -x; 
    } 


    // zero case 
    if(x == 0){ 
    return 0; 
    } 
    if (x == 0x80000000){ 
    return 0xcf000000; 
    } 

    int shiftsNeeded = 0; 

    int counter = 0; 
    while(((absValOfX >> counter) & 1) != 1 && shiftsNeeded < 32){ 

    counter++; 
    shiftsNeeded++; 
    } 

    unsigned exponent = shiftsNeeded + 127; 

    unsigned result = (xIsNegative << 31) | (exponent << 23); 

    return result; 

Aquí está el error que consigo en este caso (creo que tengo más allá del último error):

ERROR: Test float_from_int(-2139095040[0x80800000]) failed... 
...Gives -889192448[0xcb000000]. Should be -822149120[0xceff0000] 

puede ser útil saber que: absValOfX = 7f800000 (usando printf)

EDIT # 4: Ah, estoy encontrando que el exponente está equivocado, necesito contar desde la izquierda, luego restar 32 creo.

editar # 5: Empecé terminado, ahora tratando de hacer frente a los problemas de redondeo extraños ...

if (x == 0){ 
    return 0; // 0 is a special case because it has no 1 bits 
    } 
    if (x >= 0x80000000 && x <= 0x80000040){ 
    return 0xcf000000; 
    } 
    // Save the sign bit of the input and take the absolute value of the input. 
    unsigned signBit = 0; 
    unsigned absX = (unsigned)x; 
    if (x < 0) 
    { 
     signBit = 0x80000000u; 
     absX = (unsigned)-x; 
    } 

    // Shift the input left until the high order bit is set to form the mantissa. 
    // Form the floating exponent by subtracting the number of shifts from 158. 
    unsigned exponent = 158; 
    while ((absX & 0x80000000) == 0) 
    { 
     exponent--; 
     absX <<= 1; 
    } 

    unsigned negativeRoundUp = (absX >> 7) & 1 & (absX >> 8); 

    // compute mantissa 
    unsigned mantissa = (absX >> 8) + ((negativeRoundUp) || (!signBit & (absX >> 7) & (exponent < 156))); 
    printf("absX = %x, absX >> 8 = %x, exponent = %i, mantissa = %x\n", absX, (absX >> 8), exponent, mantissa); 
    // Assemble the float from the sign, mantissa, and exponent. 
    return signBit | ((exponent << 23) + (signBit & negativeRoundUp)) | ((mantissa) & 0x7fffff); 

-

absX = fe000084, absX >> 8 = fe0000, exponent = 156, mantissa = fe0000 
ERROR: Test float_from_int(1065353249[0x3f800021]) failed... 
...Gives 1316880384[0x4e7e0000]. Should be 1316880385[0x4e7e0001] 

editar # 6

hizo de nuevo, todavía , el redondeo no funciona correctamente. He tratado de cortar a algunos redondeo, pero no va a funcionar ...

unsigned float_from_int(int x) { 






    /* 
    If N is negative, negate it in two's complement. Set the high bit (2^31) of the result. 
    If N < 2^23, left shift it (multiply by 2) until it is greater or equal to. 
    If N ≥ 2^24, right shift it (unsigned divide by 2) until it is less. 
    Bitwise AND with ~2^23 (one's complement). 
    If it was less, subtract the number of left shifts from 150 (127+23). 
    If it was more, add the number of right shifts to 150. 
    This new number is the exponent. Left shift it by 23 and add it to the number from step 3. 
    */ 

    printf("---------------\n"); 
    //printf("x = %i (%x), -x = %i, (%x)\n", x, x, -x, -x); 
    if(x == 0){ 
    return 0; 
    } 

    if(x == 0x80000000){ 
    return 0xcf000000; 
    } 

    // If N is negative, negate it in two's complement. Set the high bit of the result 
    unsigned signBit = 0; 

    if (x < 0){ 
    signBit = 0x80000000; 
    x = -x; 
    } 

    printf("abs val of x = %i (%x)\n", x, x); 

    int roundTowardsZero = 0; 
    int lastDigitLeaving = 0; 
    int shiftAmount = 0; 
    int originalAbsX = x; 

    // If N < 2^23, left shift it (multiply it by 2) until it is great or equal to. 
    if(x < (8388608)){ 
    while(x < (8388608)){ 
     //printf(" minus shift and x = %i", x); 
     x = x << 1; 
     shiftAmount--; 
    } 
    } // If N >= 2^24, right shfit it (unsigned divide by 2) until it is less. 
else if(x >= (16777215)){ 
    while(x >= (16777215)){ 

     /*if(x & 1){ 
     roundTowardsZero = 1; 
     printf("zzz Got here ---"); 
     }*/ 

     lastDigitLeaving = (x >> 1) & 1; 
     //printf(" plus shift and x = %i", x); 
     x = x >> 1; 
     shiftAmount++; 

    } 
    //Round towards zero 
    x = (x + (lastDigitLeaving && (!(originalAbsX > 16777216) || signBit))); 




    printf("x = %i\n", x); 
    //shiftAmount = shiftAmount + roundTowardsZero; 
    } 

    printf("roundTowardsZero = %i, shiftAmount = %i (%x)\n", roundTowardsZero, shiftAmount, shiftAmount); 

    // Bitwise AND with 0x7fffff 
x = x & 0x7fffff; 

    unsigned exponent = 150 + shiftAmount; 

    unsigned rightPlaceExponent = exponent << 23; 

    printf("exponent = %i, rightPlaceExponent = %x\n", exponent, rightPlaceExponent); 

    unsigned result = signBit | rightPlaceExponent | x; 

    return result; 
+0

¿Debe 'absValOfX' estar sin firmar? – GWW

+0

No estoy seguro, pero no creo que podamos enviar un int a unsigned. Además, no importaría ¿verdad? ¿Porque un int positivo se comporta de forma similar a un unsigned? (No sé) –

+0

No estoy seguro de qué decir, pero a veces cuando haces cambios de bits y cosas en enteros con signo pueden suceder cosas extrañas. Otro problema es que el valor de su resultado nunca será negativo porque no está firmado. – GWW

Respuesta

3

El problema es que el más bajo es int -2147483648, pero el más alto es el 2147483647, así que no hay valor absoluto de -2147483648. Mientras que usted podría trabajar alrededor de ella, yo sólo hago un caso especial para que un patrón de bits (como lo hace el 0):

if (x == 0) 
    return 0; 
if (x == -2147483648) 
    return 0xcf000000; 

El otro problema es que ha copiado un algoritmo que sólo funciona para los números del 0 a 32767. Más abajo en el artículo, explican cómo expandirlo a todos los niveles, pero usa operaciones que probablemente no puedas usar.

Recomendaría escribirlo desde cero en función del algoritmo mencionado en su edición. Aquí hay una versión en C# que redondea hacia 0:

uint float_from_int(int x) 
{ 
    if (x == 0) 
     return 0; // 0 is a special case because it has no 1 bits 

    // Save the sign bit of the input and take the absolute value of the input. 
    uint signBit = 0; 
    uint absX = (uint)x; 
    if (x < 0) 
    { 
     signBit = 0x80000000u; 
     absX = (uint)-x; 
    } 

    // Shift the input left until the high order bit is set to form the mantissa. 
    // Form the floating exponent by subtracting the number of shifts from 158. 
    uint exponent = 158; 
    while ((absX & 0x80000000) == 0) 
    { 
     exponent--; 
     absX <<= 1; 
    } 

    // compute mantissa 
    uint mantissa = absX >> 8; 

    // Assemble the float from the sign, mantissa, and exponent. 
    return signBit | (exponent << 23) | (mantissa & 0x7fffff); 
} 
+0

Bien, estoy más cerca de la respuesta ahora! ¡Gracias! Recibí un error adicional en el que estoy trabajando ahora: "ERROR: Prueba float_from_int (8388608 [0x800000]) error ... ... Da 0 [0x0]. Debe ser 1258291200 [0x4b000000]" (ver editar a post) –

+0

Implementé su algoritmo, pero obtengo este error: "ERROR: Test float_from_int (-2147483647 [0x80000001]) failed ... ... Da -822083585 [0xceffffff].Debería ser -822083584 [0xcf000000] " He estado tratando de lidiar con estas cuestiones de redondeo desde hace un tiempo. Estoy en mi ingenio y cuestionando mi cordura. Aprecio que te tomes el tiempo para ayudar, gracias usted –

+0

@ Plata: es posible que desee hacer otra pregunta sobre cómo implementar el método de redondeo que necesita. – Gabe

0

Tratar con 0x80000000 es bastante fácil:

int xIsNegative = 0; 
unsigned int absValOfX = x; 

if (x < 0) 
{ 
    xIsNegative = 1; 
    absValOfX = -(unsigned int)x; 
} 

Se deshace de carcasa especial -2147483648 ya que el valor es representable como un valor sin signo, y absValOfX siempre debe ser positivo

+0

Esto normalmente causa un desbordamiento y un comportamiento indefinido, porque el tipo de '- x' es 'int', y, cuando x es -2,147,483,648, el valor matemático de -x sería de 2,147,483,648, pero eso no se puede representar en un entero de 32 bits con signo. –

+0

@EricPostpischil, buen punto; Actualicé el código para tomar el negativo de un número sin firmar, que todavía es representable. – MSN

+0

Dice algo sobre el estándar C que negar un número firmado es incorrecto, y la solución es negar un número sin firmar. –

1

La formulación básica del algoritmo es determinar los bits de signo, exponente y mantisa, y luego empacar el resultado en un número entero. Al dividirlo de esta manera, es más fácil separar claramente las tareas en código y hace que la solución del problema (y la prueba de su algoritmo) sea mucho más fácil.

El bit de signo es el más fácil, y deshacerse de él facilita la búsqueda del exponente. Puede distinguir cuatro casos: 0, 0x80000000, [-0x7ffffff, -1] y [1, 0x7fffffff]. Los primeros dos son casos especiales, y puede obtener trivialmente el bit de signo en los dos últimos casos (y el valor absoluto de la entrada). Si vas a enviar a unsigned, puedes salirte con la oferta no especial 0x80000000 como mencioné en un comentario.

A continuación, encuentre el exponente: hay una forma fácil (y costosa) de bucles, y una forma más rápida pero más complicada de hacerlo. Mi página favorita absoluta para esto es bit hacks page de Sean Anderson. Uno de los algoritmos muestra una forma muy rápida sin bucles de encontrar el registro de un entero en solo siete operaciones.

Una vez que conozca el exponente, encontrar la mantisa es fácil. Simplemente suelta el primer bit, luego cambia el resultado a la izquierda o a la derecha dependiendo del valor del exponente.

Si utiliza el algoritmo de registro rápido , probablemente pueda terminar con un algoritmo que utiliza no más de 20 operaciones.

+0

Trabajando a través de las ideas en la publicación en este momento, actualizaré cuando avance –