¿Cuál es el primer doble que se desvía de su correspondiente tiempo por Delta?

StackOverflow https://stackoverflow.com/questions/732612

  •  06-09-2019
  •  | 
  •  

Pregunta

Quiero saber el primer doble desde arriba 0d que se desvía por el tiempo del "mismo valor" por algunos delta, por ejemplo 1e-8. Estoy fallando aquí, sin embargo. Estoy tratando de hacer esto en C, aunque yo suelo usar lenguajes administrados, por si acaso. Por favor, ayuda.


#include <stdio.h>
#include <limits.h>
#define DELTA 1e-8

int main() {
    double d = 0; // checked, the literal is fine
    long i;
    for (i = 0L; i < LONG_MAX; i++) {
         d=i; // gcc does the cast right, i checked
         if (d-i > DELTA || d-i < -DELTA) {
              printf("%f", d);
              break;
         }
    }
}

supongo que la cuestión es que di moldes de i para doblar y por lo tanto d == I y entonces la diferencia es siempre 0. ¿Qué más puedo detectar esto correctamente - Yo prefiero la diversión C colada sobre cuerdas que comparan , lo que llevaría para siempre.

RESPUESTA : es exactamente como esperábamos. 2 ^ 53 + 1 = 9007199254740993 es el primer punto de diferencia de acuerdo con herramientas C / UNIX / POSIX estándar. Gracias tanto a personas por su programa. Y creo que las matemáticas gana de nuevo.

¿Fue útil?

Solución

Dobles en IEE754 tiene una precisión de 52 bits que significa que pueden almacenar números de precisión hasta (al menos) 2 51 .

Si sus posiciones largas son de 32 bits, que sólo tienen el rango (positivo) 0 a 2 31 lo que no hay largo de 32 bits que no se puede representar exactamente como un doble. Para una de 64 bits de largo, será (aproximadamente) 2 52 así que estaría empezando por allí, no en cero.

Puede utilizar el siguiente programa para detectar cuando empiezan a producirse los fallos. Una versión anterior que se había basado en el hecho de que el último dígito de un número que duplica de forma continua sigue la secuencia {2,4,8,6}. Sin embargo, he optado finalmente utilizar un conocido (bc) herramienta de confianza para comprobar el número entero, no sólo el último dígito.

Tenga en cuenta que este puede verán afectados por las acciones de sprintf() en lugar de la precisión real de los dobles (no me lo creo, personalmente, ya que no tenía problemas con ciertos números de hasta 2 < sup> 143 ).

Este es el programa:

#include <stdio.h>
#include <string.h>

int main() {
    FILE *fin;
    double d = 1.0; // 2^n-1 to avoid exact powers of 2.
    int i = 1;
    char ds[1000];
    char tst[1000];

    // Loop forever, rely on break to finish.
    while (1) {
        // Get C version of the double.
        sprintf (ds, "%.0f", d);

        // Get bc version of the double.
        sprintf (tst, "echo '2^%d - 1' | bc >tmpfile", i);
        system(tst);
        fin = fopen ("tmpfile", "r");
        fgets (tst, sizeof (tst), fin);
        fclose (fin);
        tst[strlen (tst) - 1] = '\0';

        // Check them.
        if (strcmp (ds, tst) != 0) {
            printf( "2^%d - 1 <-- bc failure\n", i);
            printf( "   got       [%s]\n", ds);
            printf( "   expected  [%s]\n", tst);
            break;
        }

        // Output for status then move to next.
        printf( "2^%d - 1 = %s\n", i, ds);
        d = (d + 1) * 2 - 1;  // Again, 2^n - 1.
        i++;
    }
}

Esto guarda el ir hasta que:

2^51 - 1 = 2251799813685247
2^52 - 1 = 4503599627370495
2^53 - 1 = 9007199254740991
2^54 - 1 <-- bc failure
   got       [18014398509481984]
   expected  [18014398509481983]

, que es de donde yo esperaba que fallar.

Como acotación al margen, que los números de la forma utilizada originalmente 2 n pero eso me tiene hasta:

2^136 = 87112285931760246646623899502532662132736
2^137 = 174224571863520493293247799005065324265472
2^138 = 348449143727040986586495598010130648530944
2^139 = 696898287454081973172991196020261297061888
2^140 = 1393796574908163946345982392040522594123776
2^141 = 2787593149816327892691964784081045188247552
2^142 = 5575186299632655785383929568162090376495104
2^143 <-- bc failure
   got       [11150372599265311570767859136324180752990210]
   expected  [11150372599265311570767859136324180752990208]

con el tamaño de un doble que es de 8 bytes (comprobado con sizeof). Resultó que estos números eran de la "1000..." forma binaria que puede ser representada por mucho más tiempo con dobles. Fue entonces cuando me cambié a usar 2 n -1 para obtener un mejor patrón de bits:. Todos los bits de un solo

Otros consejos

El primer largo para ser 'mal' cuando se convierte a un doble no va a estar fuera por 1e-8, que estará fuera por 1. Mientras el doble puede ajustarse a la larga en la mantisa, que representarán con precisión .

Yo recuerdo exactamente cuántos bits tiene un doble de precisión vs compensado, pero que diría el tamaño máximo que podría representar. El primer tiempo para estar mal debe tener la forma binaria 10000 ..., por lo que puede resultar mucho más rápido comenzando en 1 e izquierda-shifting.

Wikipedia dice 52 bits en la mantisa, sin contar la partida implícito 1. Eso debería significar el primer tiempo para ser echado a un valor diferente es 2 ^ 53.

A pesar de que estoy indeciso mencionar Fortran 95 y sucesores en esta discusión, voy a mencionar que Fortran ya que la norma 1990 ha ofrecido una función intrínseca espaciador que le indica cuál es la diferencia entre el formato REAL representables son alrededor de un real dado. Se podría hacer una búsqueda binaria sobre esto, deteniéndose cuando la separación (X)> DELTA. Para los compiladores que utilizan el mismo modelo de punto flotante como el que usted está interesado en la (probable que sea el estándar IEEE754), usted debe obtener los mismos resultados.

Off lado, pensé que los dobles podrían representar todos los números enteros (dentro de sus límites) exactamente.

Si ese no es el caso, entonces usted va a querer echar tanto iyd a algo con más precisión que cualquiera de ellos. Tal vez un largo doble funcionará.

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