2009-08-07 28 views
19

Estoy haciendo cálculos científicos de alta precisión. Al buscar la mejor representación de varios efectos, sigo encontrando razones para querer obtener el siguiente número de precisión doble más alto (o más bajo) disponible. Básicamente, lo que quiero hacer es agregar uno al bit menos significativo en la representación interna de un doble.próximo más alto/más bajo IEEE precisión doble número

La dificultad es que el formato IEEE no es totalmente uniforme. Si se utilizara un código de bajo nivel y se agregara uno al bit menos significativo, el formato resultante podría no ser el siguiente doble disponible. Podría, por ejemplo, ser un número de caso especial como PositiveInfinity o NaN. También están los valores subnormales, que no pretendo entender, pero que parecen tener patrones de bits específicos diferentes del patrón "normal".

Hay disponible un valor "épsilon", pero nunca he entendido su definición. Como los valores dobles no están espaciados uniformemente, no se puede agregar ningún valor a un doble para obtener el siguiente valor más alto.

Realmente no entiendo por qué IEEE no ha especificado una función para obtener el siguiente valor más alto o más bajo. No puedo ser el único que lo necesita.

Hay una manera de obtener el siguiente valor (sin algún tipo de bucle que intente agregar valores cada vez más pequeños).

+2

IEEE-754 * ha * especificado tales funciones - 'nextUp' y' nextDown' como se requiere en la sección 5.3.1 del estándar revisado (2008), y la función 'nextafter' anterior recomendada por el original (1985) estándar, y requerido en C99. –

Respuesta

1

No estoy seguro de estar siguiendo su problema. Sin duda, el estándar IEEE es totalmente uniforme? Por ejemplo, mire este extracto del wikipedia article para números de doble precisión.

3ff0 0000 0000 0000 = 1 
3ff0 0000 0000 0001 = 1.0000000000000002, the next higher number > 1 
3ff0 0000 0000 0002 = 1.0000000000000004 

¿Qué hay de malo en simplemente incrementar el bit menos significativo, en una representación binaria o hexadecimal?

En cuanto a los números especiales van (infinito, NaN, etc.), están bien definidos, y no hay muchos de ellos. Los límites están definidos de manera similar.

Dado que obviamente ha investigado esto, creo que tengo el extremo equivocado. Si esto no es suficiente para su problema, ¿podría tratar de aclarar lo que quiere lograr? ¿Cuál es tu objetivo aquí?

+0

¿Funcionaría eso en casos donde el exponente tendría que aumentar? –

+0

Mi objetivo es hacer esto limpiamente, preferiblemente desde C#, pero voy a rebajar a nivel de bits si es necesario. El problema es que el estándar IEEE no está en el dominio público, y no puedo comprarlo. El estándar define los patrones de bits para el caso que muestra, pero también para todos los números inusuales (como las subnormales). Uno no debería tener que conocer todos los detalles de todos los formatos numéricos para realizar esta tarea. Pero si volteas partes tú mismo, tendrías que hacerlo. ¿Qué pasa si el 'próximo' número es un subnormal? ¡A menos que conozca todas las reglas, NO PUEDE llegar! –

+1

@Mark T: Ok, entiendo tu problema ahora. ¡No me había dado cuenta de que el estándar no estaba disponible gratuitamente (increíble)! Aquí hay implementaciones de varias funciones, incluyendo dnxtaft.f, que devuelve el siguiente valor de coma flotante en la dirección de x. Tal vez esto ayude? http://www.math.utah.edu/~beebe/software/ieee/ –

12

Existen funciones disponibles para hacer exactamente eso, pero pueden depender del idioma que use. Dos ejemplos:

  • si tiene acceso a una biblioteca matemática C99 decente, puede utilizar nextafter (y su flotador y variantes dobles largos, nextafterf y nextafterl); o la familia nexttoward (que toma un doble largo como segundo argumento).

  • si se escribe Fortran, que tienen la nearest intrínseca disponibles

Si no puede acceder a ellos directamente desde su idioma, también se puede ver cómo están implementados en libre disposición, tales como this one.

2

Sí, hay una manera. En C#:

 public static double getInc (double d) 
     { 
       // Check for special values 
       if (double.IsPositiveInfinity(d) || double.IsNegativeInfinity(d)) 
        return d; 
       if (double.IsNaN(d)) 
        return d; 

       // Translate the double into binary representation 
       ulong bits = (ulong)BitConverter.DoubleToInt64Bits(d); 
       // Mask out the mantissa bits 
       bits &= 0xfff0000000000000L; 
       // Reduce exponent by 52 bits, so subtract 52 from the mantissa. 
       // First check if number is great enough. 
       ulong testWithoutSign = bits & 0x7ff0000000000000L; 
       if (testWithoutSign > 0x0350000000000000L) 
        bits -= 0x0350000000000000L; 
       else 
        bits = 0x0000000000000001L; 
       return BitConverter.Int64BitsToDouble((long)bits); 
} 

El aumento se pueden añadir y restar.

+0

Esto no funciona Compilar, y no creo que esté utilizando adecuadamente el método 'BitConverter.DoubleToInt64Bits' correctamente de todos modos. Si desea obtener la representación de bytes de un número, debe usar 'BitConverter.GetBytes' (pero luego debe asegurarse de aumentar o disminuir el exponente, si es necesario). –

+0

No se compila porque C# no permite mezclar constantes y variables ulong/long (lo cual es estúpido para los operadores de bits). Y pensó que no, el método BitConverter devuelve la estructura de bytes internos del doble en el formato IEEE. –

5

Como dice Thorsten S., esto se puede hacer con la clase BitConverter, pero su método supone que el método DoubleToInt64Bits devuelve el byte de estructura interna de la double, que no lo hace. El entero devuelto por ese método en realidad devuelve el número de dobles representables entre 0 y el tuyo. Es decir. el doble positivo más pequeño está representado por 1, el siguiente doble más grande es 2, etc. Los números negativos comienzan en long.MinValue y desaparecen de 0d.

Así que usted puede hacer algo como esto:

public static double NextDouble(double value) { 

    // Get the long representation of value: 
    var longRep = BitConverter.DoubleToInt64Bits(value); 

    long nextLong; 
    if (longRep >= 0) // number is positive, so increment to go "up" 
     nextLong = longRep + 1L; 
    else if (longRep == long.MinValue) // number is -0 
     nextLong = 1L; 
    else // number is negative, so decrement to go "up" 
     nextLong = longRep - 1L; 

    return BitConverter.Int64BitsToDouble(nextLong); 
} 

Esto no trata con Infinity y NaN, pero se puede comprobar para aquellos y tratar con ellos como usted quiera, si estás preocupado por eso.

+0

Veo que está usando mi código porque el argumento es value, pero BitConverter.DoubleToInt64Bits obtiene "d" como argumento. Tenía reservas acerca de simplemente agregar uno porque el formato IEEE separa el exponente y el significado, pero como tiene un bit oculto , su función está de hecho bien por lo que puedo ver. –

6

La mayoría de los idiomas tienen funciones intrínsecas o de biblioteca para adquirir el número siguiente o anterior de precisión simple (32 bits) y/o precisión doble (64 bits).

Para los usuarios de aritmética de coma flotante de 32 y 64 bits, una buena comprensión de los constructos básicos es muy útil para evitar algunos riesgos con ellos. El estándar IEEE se aplica de manera uniforme, pero aún deja una cantidad de detalles a los implementadores. Por lo tanto, una solución universal de plataforma basada en manipulaciones de bits de las representaciones de palabras de la máquina puede ser problemática y puede depender de problemas tales como endian y demás. Si bien comprender todos los detalles sangrientos de cómo podría o debería funcionar en el nivel de bit puede demostrar destreza intelectual, es mejor usar una solución de biblioteca intrínseca que se adapte a cada plataforma y tenga una API universal en todas las plataformas admitidas.

Noté soluciones para C# y C++. Éstos son algunos de Java:

Math.nextUp:

pública doble Nextup estática (double d):

  • Devuelve el valor de coma flotante adyacente a d en la dirección de infinito positivo. Este método es semánticamente equivalente a nextAfter (d, Double.POSITIVE_INFINITY); sin embargo, una implementación nextUp puede ejecutarse más rápido que su llamada nextAfter equivalente.

Casos especiales:

  • Si el argumento es NaN, el resultado es NaN.
  • Si el argumento es infinito positivo, el resultado es positivo infinito.
  • Si el argumento es nulo, el resultado es Double.MIN_VALUE

Parámetros:

  • d - a partir de coma flotante de valor

Devuelve:

  • El valor de coma flotante adyacente más cercano al infinito positivo.

flotador public static NextUp (float f):

  • Devuelve el valor de punto flotante adyacente a f en la dirección de infinito positivo. Este método es semánticamente equivalente a nextAfter (f, Float.POSITIVE_INFINITY); sin embargo, una implementación nextUp puede ejecutarse más rápido que su llamada nextAfter equivalente.

Casos especiales:

  • Si el argumento es NaN, el resultado es NaN.
  • Si el argumento es infinito positivo, el resultado es positivo infinito.
  • Si el argumento es nulo, el resultado es Float.MIN_VALUE

Parámetros:

  • f - a partir de coma flotante de valor

devuelve:

  • El valor de coma flotante adyacente más cercano al infinito positivo.

Las dos siguientes son un poco más complejas de usar. Sin embargo, una dirección hacia cero o hacia el infinito positivo o negativo parecen los usos más probables y útiles. Otro uso es ver que existe un valor intermedio entre dos valores. Uno puede determinar cuántos existen entre dos valores con un bucle y un contador. Además, parece que, junto con los métodos nextUp, podrían ser útiles para incrementos/disminuciones en los ciclos for.

Math.nextAfter:

pública nextafter doble (doble inicio, doble efecto) estática

  • Devuelve el número de coma flotante adyacente al primer argumento en el sentido de el segundo argumento Si ambos argumentos se comparan como , se devuelve el segundo argumento.

casos especiales:

  • Si alguno de los argumentos es NaN, entonces se devuelve NaN.
  • Si ambos argumentos están firmados ceros, la dirección se devuelve sin cambios (como implica el requisito de devolver el segundo argumento si los argumentos se comparan como iguales).
  • Si el inicio es ± Double.MIN_VALUE y la dirección tiene un valor tal que el resultado debe tener una magnitud menor, se devuelve un cero con el mismo signo como inicio.
  • Si el inicio es infinito y la dirección tiene un valor tal que el resultado debe tener una magnitud menor, Double.MAX_VALUE con el mismo signo cuando se devuelve el inicio.
  • Si el inicio es igual a ± Double.MAX_VALUE y la dirección tiene un valor de modo que el resultado debe tener una magnitud mayor, un infinito con se devuelve el mismo signo que start.

Parámetros:

  • inicio - a partir de valores de punto flotante
  • dirección - valor que indica cuál de los vecinos de inicio o comenzar debe ser devuelto

Devuelve:

  • El punto flotante número adyacente para comenzar en la dirección de dirección.

pública (inicio flotador, doble efecto) de flotación estática nextafter

  • Devuelve el número de coma flotante adyacente al primer argumento en la dirección del segundo argumento. Si ambos argumentos se comparan como , se devuelve igual valor equivalente al segundo argumento.

casos especiales:

  • Si alguno de los argumentos es NaN, entonces se devuelve NaN.
  • Si ambos argumentos tienen ceros firmados, se devuelve un valor equivalente a la dirección .
  • Si el inicio es ± Float.MIN_VALUE y la dirección tiene un valor tal que el resultado debe tener una magnitud menor, se devuelve un cero con el mismo signo como inicio.
  • Si el inicio es infinito y la dirección tiene un valor tal que el resultado debe tener una magnitud menor, Float.MAX_VALUE con el mismo signo como inicio se devuelve.
  • Si el inicio es igual a ± Float.MAX_VALUE y la dirección tiene un valor como que el resultado debe tener una magnitud mayor, se devuelve un infinito con el mismo signo como inicio.

Parámetros:

  • inicio - valor de coma flotante a partir
  • dirección - valor que indica cuál de los vecinos de inicio o de inicio debe ser devuelto

Devuelve:

  • El número de punto flotante adyacente al inicio en el dirección de dirección
1

Con respecto a la función epsilon, es una estimación de qué tan lejos podría estar la aproximación de un valor decimal del doble binario. Esto se debe a que, para números decimales positivos o negativos muy grandes o números decimales positivos o negativos muy pequeños, muchos de ellos se asignan a la misma representación binaria que un doble. Pruebe números decimales muy, muy grandes o muy, muy pequeños, cree dobles de ellos y luego vuelva a transformarlos en un número decimal. Descubrirá que no obtendrá el mismo número decimal, sino el que el doble está más cerca en su lugar.

Para los valores cercanos (cerca del vasto rango de valores decimales que los dobles pueden representar) 1 o -1, épsilon será cero o muy, muy pequeño. Para valores que progresivamente se dirigen hacia + o - infinito o cero, epsilon comenzará a crecer. En valores extremadamente cercanos a cero o infinitos, épsilon será muy grande porque las representaciones binarias disponibles para los valores decimales en esos rangos son muy, muy escasos.

Cuestiones relacionadas