Pregunta

He leído recientemente un poco en el estándar IEEE 754 y la arquitectura x87. Yo estaba pensando en utilizar NaN como un "valor faltante" en algún código de cálculo numérico que estoy trabajando, y esperaba que el uso de señalización NaN me permitiría capturar una excepción de punto flotante en los casos en el que no desea continuar con "los valores que faltan." Por el contrario, me gustaría utilizar tranquila NaN para permitir que el "valor faltante" se propague a través de un cálculo. Sin embargo, NaN de señalización no funcionan como pensé que iban a partir de la documentación (muy limitado) que existe en ellos.

A continuación se presenta un resumen de lo que sé (todo esto utilizando x87 y VC ++):

  • _EM_INVALID (el IEEE excepción "no válido") controla el comportamiento de la x87 cuando se enfrentan a NaNs
  • Si se enmascara _EM_INVALID (la excepción es discapacitado), se genera una excepción y operaciones puede volver NaN. Una operación que implica la señalización NaN no provocar una excepción que se lance, pero será convertida a la tranquilidad NaN.
  • Si _EM_INVALID es desenmascarado (activar excepción), una operación no válida (por ejemplo, sqrt (-1)) produce una excepción no válida a ser lanzado.
  • El x87 no genera señalización NaN.
  • Si _EM_INVALID es desenmascarado, cualquier uso de una señalización NaN (incluso inicializar una variable con el mismo) provoca una excepción inválida a ser lanzado.

La biblioteca estándar proporciona una manera de acceder a los valores NaN:

std::numeric_limits<double>::signaling_NaN();

y

std::numeric_limits<double>::quiet_NaN();

El problema es que no veo utilidad alguna para la señalización NaN. Si _EM_INVALID está enmascarado se comporta exactamente igual que NaN. Dado que no NaN es comparable a cualquier otro NaN, no hay diferencia lógica.

Si _EM_INVALID es no enmascarado (excepción está activado), entonces ni siquiera se puede inicializar una variable con una señalización NaN: double dVal = std::numeric_limits<double>::signaling_NaN(); porque esto produce una excepción (el valor NaN de señalización se carga en un x87 registro para almacenarlo en la dirección de memoria).

Usted puede pensar lo siguiente como lo hice:

  1. Máscara _EM_INVALID.
  2. inicializar la variable con señalización NaN.
  3. Unmask_EM_INVALID.

Sin embargo, el paso 2 hace que la señalización NaN a convertir en una tranquila NaN, usos posteriores de modo que se no excepciones causa que se arrojen! Así WTF?!

¿Hay alguna utilidad o propósito que sea una señalización NaN? Entiendo una de las intenciones originales era para inicializar la memoria con él, así que el uso de un valor de punto flotante no inicializado podría ser capturado.

Puede alguien decirme si estoy perdiendo algo?


EDIT:

Para ilustrar mejor lo que había esperado hacer, aquí hay un ejemplo:

Considere la realización de operaciones matemáticas en un vector de datos (dobles). Para algunas operaciones, quiero permitir que el vector contiene un "valor faltante" (pretender que esto corresponde a una columna de hoja de cálculo, por ejemplo, en el que algunas de las células no tienen un valor, pero su existencia es significativa). Para algunas operaciones, yo no quieren permitir que el vector contiene un "valor faltante." Tal vez quiero tomar un curso de acción diferente si un "valor faltante" está presente en el conjunto - tal vez realizar una operación diferente (por lo que este no es un estado no válido para estar en)

.

Este código original sería algo como esto:

const double MISSING_VALUE = 1.3579246e123;
using std::vector;

vector<double> missingAllowed(1000000, MISSING_VALUE);
vector<double> missingNotAllowed(1000000, MISSING_VALUE);

// ... populate missingAllowed and missingNotAllowed with (user) data...

for (vector<double>::iterator it = missingAllowed.begin(); it != missingAllowed.end(); ++it) {
    if (*it != MISSING_VALUE) *it = sqrt(*it); // sqrt() could be any operation
}

for (vector<double>::iterator it = missingNotAllowed.begin(); it != missingNotAllowed.end(); ++it) {
    if (*it != MISSING_VALUE) *it = sqrt(*it);
    else *it = 0;
}

Tenga en cuenta que el cheque por el "valor faltante" debe realizarse cada repetición del bucle . Si bien entiendo en la mayoría de los casos, la función sqrt (o cualquier otra operación matemática) es probable que opacar esta comprobación, hay casos en que la operación es mínima (tal vez sólo una adición) y el cheque es costoso. Por no mencionar el hecho de que el "valor faltante" toma un valor de entrada legal fuera de juego y podría causar errores si un cálculo legítimamentellega a ese valor (poco probable, aunque puede ser). También para ser técnicamente correcto, los datos de entrada de usuario se deben comprobar en contra de ese valor y un curso de acción apropiado deben ser tomadas. Encuentro que esta solución poco elegante y menos que óptima en cuanto al rendimiento. Este es el código rendimiento crítico, y definitivamente no tienen el lujo de estructuras de datos en paralelo u objetos de elementos de datos de algún tipo.

La versión NaN se vería así:

using std::vector;

vector<double> missingAllowed(1000000, std::numeric_limits<double>::quiet_NaN());
vector<double> missingNotAllowed(1000000, std::numeric_limits<double>::signaling_NaN());

// ... populate missingAllowed and missingNotAllowed with (user) data...

for (vector<double>::iterator it = missingAllowed.begin(); it != missingAllowed.end(); ++it) {
    *it = sqrt(*it); // if *it == QNaN then sqrt(*it) == QNaN
}

for (vector<double>::iterator it = missingNotAllowed.begin(); it != missingNotAllowed.end(); ++it) {
    try {
        *it = sqrt(*it);
    } catch (FPInvalidException&) { // assuming _seh_translator set up
        *it = 0;
    }
}

Ahora la comprobación explícita se elimina y el rendimiento debe ser mejorado. Creo que esto sería todo el trabajo si yo podría inicializar el vector sin tocar los registros de FPU ...

Además, me imagino ningún control de la ejecución sqrt precien de NaN NaN y devuelve inmediatamente.

¿Fue útil?

Solución

Según entiendo, el propósito de señalización NaN es inicializar estructuras de datos, pero, por supuesto runtime inicialización en C corre el riesgo de tener el NaN cargado en un registro de flotador como parte de la inicialización , iniciando así la señal debido a que el compilador no es consciente de que este valor flotante necesita ser copiado utilizando un registro entero.

Me gustaría que usted podría podría inicializar un valor static con un NaN de señalización, pero incluso eso requeriría un manejo especial por el compilador para evitar tener que convertir en una tranquila NaN. Usted tal vez podría utilizar un poco de magia de fundición para evitar tener que tratar como un valor flotante durante la inicialización.

Si estuviera escribiendo en ASM, esto no sería un problema. pero en C y especialmente en C ++, creo que tendrá que subvertir el sistema de tipos con el fin de inicializar una variable con NaN. Se sugiere emplear memcpy.

Otros consejos

El uso de valores especiales (incluso nulo) puede hacer que sus datos mucho fangosas y su código de una gran cantidad de Messier. Sería imposible distinguir entre un resultado y un valor QNAN QNAN "especial".

podría ser mejor mantener una estructura de datos en paralelo para realizar un seguimiento de validez, o tal vez tener sus datos de PF en un (escasa) estructura de datos diferentes sólo para mantener los datos válidos.

Este es un consejo bastante general; valores especiales son muy útiles en ciertos casos (por ejemplo, memoria o de rendimiento muy apretadas restricciones), pero a medida que el contexto se hace más grande que puede causar más problemas de lo que valen.

¿No podrías tener un uint64_t const, donde los bits se han ajustado a las de una señalización nan? siempre y cuando se lo trata como un tipo entero, el Nan señalización no es diferente de otros números enteros. Se puede escribir el lugar que desee a través del puntero de fundición a presión:

Const uint64_t sNan = 0xfff0000000000000;
Double[] myData;
...
Uint64* copier = (uint64_t*) &myData[index];
*copier=sNan | myErrorFlags;

Para obtener información sobre los bits que establece: https://www.doc.ic.ac.uk/ ~ eedwards / compsys / flotar / nan.html

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