0x7FFF

Sumas, multiplicaciones, "overflow", etc. (¿1+1=-1?)

Es común que las señales que manejemos vayan de -1.0 a 1.0; pero cuando trabajamos en punto fijo, la cosa cambia.

En 16 bits, el mayor número positivo que podemos guardar es un 0x7FFF. El número más negativo que podemos tener es 0x8000. Normalmente decimos que 0x8000 es -1 y que 0x7FFF es casi 1.

¿Cuánto es 0x7FFF+1? Claro, es un 0x8000 (positivo), pero si sólo tenemos 16 bits 0x8000 representa un número negativo. Lo mas probable es que lo deseable sea saturar el valor; es decir, que si cualquier operación sobrepasa el 0x7FFF, se va a tomar el número más positivo como resultado.

De -1.0 a 0.999

¿Cómo se representa un número flotante en punto fijo?

El bit más significativo (MSB) vale 1 (2 elevado a la 0) y el siguiente valdría 2 elevado a la -1, etc. Veamos el siguiente pseudo-fraccional de 8 bits:

MSB   LSB
2 02-12-22-3 2-42-52-62-7
10.50.250.1250.06250.031250.0156250.0078125

Es improtante recordar que el MSB también es el que indica el signo. Por eso es que no existe el +1.0 en este formato. En este ejemplo (8 bits en formato 1.7) el máximo valor positivo que podemos tener es 0.9921875.

En C

Cuando implementamos nuestro algoritmo en C, tenemos que tener cuidado al manejar los datos.

Normalmente vamos a necesitar hacer una operación en 32 bits, validar el tamaño del resultado y asignarlo a 16 bits.

Lo mejor es hacer una "librería" de operaciones básicas. Esto lo aprendí después de revisar un código de ITU. Dicho código tenía un par de archivos llamados "basop.c" y "basop.h" (basop seguramente quiere decir "basic operations").

Las funciones imprescindibles que debe tener ese archivo son:

  • Int16 sature(Int32)
  • Int16 add(Int16, Int16)
  • Int16 mult(Int16, Int16)

Estas son funciones muy simples, pero deben garantizarnos que nuestras operaciones sean correctas y que no se desborden las variables. A veces pueden ser muy simples, como:

Int16 sature(Int32 A){
    if(A>((Int32)0x00007FFF)) return 0x7FFF;
    if(A<((Int32)0xFFFF8000)) return 0x8000;
    return A;
}

La función de suma puede ser más simple aún:

#define SUMA(A,B)    ( sature( ((Int32)A) + ((Int32)B) ) )

¿Que estamos haciendo ahí? Estamos convirtiendo A y B a 32 bits, sumándolos y luego saturándolos.

En el DSP 

En un DSP la cosa puede ser muy distinta, ya que puede haber diferentes modos de saturación. Hay DSPs que te permiten decidir cómo manejar un 0x7FFF+1.

Recordemos que si el procesador es de 16 bits, su acumulador probablemente será de 32 bits al menos.

Aquí no basta con confiar en el compilador de C, sino que hay que hacer algunas pruebas. Puede ser que nuestra librería de operaciones básicas (basop) sea mucho mas simple que lo que sería en C.

1*1 = 1

O al menos eso dice la teoría.

Si dijimos que el 0x7FFF es casi 1, 0x7FFF * 0x7FFF debería ser también casi uno ¿no? (pero un "casi uno" más pequeño). Sin embargo, la multiplicación aritmética nos da como resultado 0x3FFF0000. No nos conviene simplemente saturar porque 0.9 * 0.9 no es igual a 0.9. Es igual a 0.81 (acuérdense que el resultado debe ser un "casi uno" mas pequeño).

Aquí lo que tenemos que hacer es un "shift" a la derecha de 15 posiciones.

Otro ejemplo: 0.5 * 0.5 = 0.25. Si saturamos, tenemos que 0x4000 * 0x4000 = 0x10000000 y saturaríamos a 0x7FFF, lo cual no es cierto. En cambio al recorrer a la derecha 15 posiciones, tenemos un 0x2000 que si es un 0.25.

Otros links

Otro link que explica la forma en que manejamos fracciones en procesadores de punto fijo es el siguiente:

  • http://zone.ni.com/devzone/cda/tut/p/id/3115


[Regresar]