Pregunta

Estos son los objetivos que estoy tratando de lograr:

  • I necesidad de paquete de 32 bits IEEE flota en 30 bits.
  • I quiero hacer esto disminuyendo el tamaño de mantisa por 2 bits.
  • La operación en sí debe ser lo más rápido posible.
  • Soy consciente de que un poco de precisión se pierde, y esto es aceptable.
  • Sería una ventaja, si esta operación no arruinaría casos especiales como SNAN, QNAN, infinitos, etc, pero estoy dispuesto a sacrificar esta velocidad más.

Creo que esta pregunta consta de dos partes:

1) ¿Puedo simplemente borrar los bits menos significativos de mantisa? He intentado esto, y hasta ahora funciona, pero tal vez estoy buscando problemas ... Algo así como:

float f;
int packed = (*(int*)&f) & ~3;
// later
f = *(float*)&packed;

2) Si hay casos en que 1) se producirá un error, entonces ¿cuál sería la forma más rápida de lograrlo?

Gracias de antemano

¿Fue útil?

Solución 4

No se puede seleccionar cualquiera de las respuestas como la definitiva, porque la mayoría de ellos tienen información válida, pero no es lo que estaba buscando. Así que voy a resumir mis conclusiones.

El método de conversión que he publicado en parte de mi pregunta 1) es claramente erróneo por el estándar C ++, por lo que otros métodos de bits de extracto de flotador se deben utilizar.

Y lo más importante ... por lo que yo entiendo de la lectura de las respuestas y de otras fuentes sobre IEEE754 flotadores, está bien para soltar los bits menos significativos de la mantisa. Va a afectar en su mayoría sólo de precisión, con una excepción: sNaN. Desde sNaN está representado por conjunto exponente a 255, y mantisa! = 0, no puede haber situación en la mantisa habría <= 3, y dejando caer dos últimos bits sería convertir sNaN a +/- Infinity. Pero desde sNaN no son generados durante las operaciones de punto flotante de la CPU, que es seguro en un entorno controlado.

Otros consejos

En realidad se violan las estrictas reglas de alias (sección 3.10 de la norma C ++) con estos moldes reinterpretar. Esto probablemente va a explotar en la cara cuando se enciende las optimizaciones del compilador.

C ++ estándar, la sección 3.10 párrafo 15 dice:

  

Si un programa intenta acceder al valor almacenado de un objeto a través de un lvalue distinta de uno de los siguientes tipos el comportamiento es indefinido

     
      
  • el tipo dinámico del objeto,
  •   
  • una versión cv calificado del tipo dinámico del objeto,
  •   
  • un tipo similar al tipo dinámico del objeto,
  •   
  • un tipo que es el firmado o tipo sin signo correspondiente al tipo dinámico del objeto,
  •   
  • un tipo que se la firmó o tipo sin signo que corresponde a una versión cv calificado del tipo dinámico del objeto,
  •   
  • un tipo de agregado o unión que incluye uno de los tipos antes mencionados entre sus miembros (incluyendo, de forma recursiva, un miembro de un subagregado o unión contenida),
  •   
  • un tipo que es un tipo de clase base (posiblemente cv-calificado) del tipo dinámico del objeto,
  •   
  • un char o unsigned char tipo.
  •   

Específicamente, 3,10 / 15 no nos permite acceder a un objeto flotante a través de un lvalue de tipo unsigned int. En realidad, yo le picaron por esto. El programa que escribí dejó de funcionar después de encender optimizaciones. Al parecer, GCC no esperaba un valor-I de tipo float a un alias de lvalue de tipo int que es razonable suponer por 3.10 / 15. Las instrucciones consiguieron barajan en torno por el optimizador bajo la regla de que, si la explotación de 3,10 / 15 y que dejó de funcionar.

En la siguiente supuestos

  • flotador realmente corresponde a una de 32 bits IEEE-flotador,
  • sizeof (float) == sizeof (int)
  • unsigned int no tiene bits de relleno o representaciones trampa

usted debe ser capaz de hacerlo de esta manera:

/// returns a 30 bit number
unsigned int pack_float(float x) {
    unsigned r;
    std::memcpy(&r,&x,sizeof r);
    return r >> 2;
}

float unpack_float(unsigned int x) {
    x <<= 2;
    float r;
    std::memcpy(&r,&x,sizeof r);
    return r;
}

Esto no sufre de la "3.10-violación" y es normalmente muy rápido. Al menos trata CCG memcpy como una función intrínseca. En caso de que no necesita las funciones de trabajo con infinitos NANS, o bien números extremadamente alta magnitud incluso se puede mejorar la precisión mediante la sustitución de "r >> 2" con "(r + 1) >> 2":

unsigned int pack_float(float x) {
    unsigned r;
    std::memcpy(&r,&x,sizeof r);
    return (r+1) >> 2;
}

Esto funciona incluso si se cambia el exponente debido a un desbordamiento debido a que la mantisa IEEE-754 de codificación de mapas de valores de coma flotante consecutivos a números enteros consecutivos (ignorando +/- cero). Este mapeo realidad se aproxima a un logaritmo bastante bien.

dejando caer ciegamente los 2 bits menos significativos del flotador puede fallar por pequeño número de codificaciones NaN inusuales.

A NaN se codifica como exponente = 255, mantisa! = 0, pero IEEE-754 no dice nada acerca de lo que deben utilizarse los valores mantiassa. Si el valor de la mantisa es <= 3, se puede convertir un NaN en una infinidad!

Se debe encapsular en una estructura, para que accidentalmente no mezclar el uso del flotador marcada con "unsigned int" regular:

#include <iostream>
using namespace std;

struct TypedFloat {
    private:
        union {
            unsigned int raw : 32;
            struct {
                unsigned int num  : 30;  
                unsigned int type : 2;  
            };
        };
    public:

        TypedFloat(unsigned int type=0) : num(0), type(type) {}

        operator float() const {
            unsigned int tmp = num << 2;
            return reinterpret_cast<float&>(tmp);
        }
        void operator=(float newnum) {
            num = reinterpret_cast<int&>(newnum) >> 2;
        }
        unsigned int getType() const {
            return type;
        }
        void setType(unsigned int type) {
            this->type = type;
        }
};

int main() { 
    const unsigned int TYPE_A = 1;
    TypedFloat a(TYPE_A);

    a = 3.4;
    cout << a + 5.4 << endl;
    float b = a;
    cout << a << endl;
    cout << b << endl;
    cout << a.getType() << endl;
    return 0;
}

No se puede garantizar su portabilidad sin embargo.

¿Cuánto precisión se necesita? Si de 16 bits flotante es suficiente (suficiente para algunos tipos de gráficos), luego de flotación de 16 bits de ILM ( "medio"), parte de OpenEXR es grande, obedece todo tipo de reglas (http://www.openexr.com/ ), y usted tendrá un montón de espacio que queda después de hacer las maletas en una estructura.

Por otro lado, si se conoce el rango aproximado de los valores que van a tomar, debe considerar el punto fijo. Son más útiles que la mayoría de las personas se dan cuenta.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top