Qual é a primeira dupla que se desvia do seu correspondente de comprimento por delta?

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

  •  06-09-2019
  •  | 
  •  

Pergunta

Eu quero saber a primeira dupla a partir de cima 0d que se desvia pela longa do "mesmo valor" por alguns delta, dizem 1e-8. Estou falhando aqui embora. Eu estou tentando fazer isso em C, embora eu costumo usar idiomas gerenciados, apenas no caso. Por favor, ajuda.


#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;
         }
    }
}

Eu estou supondo que o problema é que di moldes i dobrar e, portanto, de d == i e, em seguida, a diferença é sempre 0. Como mais posso detectar isso corretamente - Eu prefiro divertido C lançando sobre strings comparando , o que levaria para sempre.

RESPOSTA : é exatamente como se esperava. 2 ^ 53 + 1 = 9007199254740993 é o primeiro ponto de diferença de acordo com a / ferramentas padrão C UNIX / POSIX. Graças muito a pax para seu programa. E eu acho que matemática vitórias novamente.

Foi útil?

Solução

Duplas em IEE754 ter uma precisão de 52 bits, o que significa que eles podem armazenar números de precisão até (pelo menos) dois 51 .

Se o seu longs são de 32 bits, eles só terão a (positivo) variam de 0 a 2 31 para que não haja de 32 bits muito tempo que não pode ser representado exatamente como um duplo. Para um 64-bit de comprimento, será (aproximadamente) 2 52 , então eu estaria começando em torno de lá, não em zero.

Você pode usar o seguinte programa para detectar onde as falhas começam a ocorrer. Uma versão anterior Eu contava com o fato de que o último dígito em um número que continuamente dobra segue a seqüência {2,4,8,6}. No entanto, optei finalmente usar um (bc) ferramenta confiável conhecido por verificar o número inteiro, não apenas o último dígito.

Tenha em mente que esta pode ser afetado pelas ações de sprintf() vez do precisão real de duplas (eu não penso assim, pessoalmente, uma vez que não tinha problemas com determinados números de até 2 < sup> 143 ).

Este é o 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++;
    }
}

Isso mantém indo até:

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

que é sobre onde eu esperava que ele falhe.

Como um números de lado, eu usei originalmente da forma 2 n , mas que me levantei para:

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]

com o tamanho de um duplo sendo 8 bytes (marcada com sizeof). Descobriu-se estes números eram da "1000..." forma binária que pode ser representado por muito mais tempo com duplos. Foi quando eu mudei para usando 2 n -1 para obter um padrão pouco melhor:. Todos os bits

Outras dicas

O primeiro longa a ser 'errado' quando fundido a uma dupla não vai estar fora por 1e-8, ele vai estar fora por 1. Enquanto a dupla pode caber a longo em seu significando, ele irá representá-lo com precisão .

Eu esqueço exatamente quantos bits a dupla tem de precisão vs offset, mas que iria dizer-lhe o tamanho máximo que poderia representar. O primeiro longa de estar errado deve ter a forma binária 10000 ..., para que possa encontrá-lo muito mais rápido, iniciando em 1 e deixou-shifting.

Wikipedia diz 52 bits do significando, sem contar a partida implícito 1. Isso deve significar o primeiro tempo para ser convertido para um valor diferente é 2 ^ 53.

Embora eu estou hesitante mencionar Fortran 95 e sucessores nesta discussão, vou mencionar que Fortran uma vez que o padrão de 1990 ofereceu uma função intrínseca ESPAÇAMENTO que lhe diz que a diferença entre REALs representáveis ??são sobre um determinado REAL. Você poderia fazer uma busca binária sobre isso, parando quando o espaçamento (X)> DELTA. Para compiladores que usam o mesmo modelo de ponto flutuante como o que você está interessado em (provavelmente será o padrão IEEE754), você deve obter os mesmos resultados.

Off mão, pensei que dobra poderia representar todos os inteiros (dentro de seus limites) exatamente.

Se isso não for o caso, então você vai querer elenco tanto i e d a algo com mais precisão do que qualquer um deles. Talvez um longo trabalho duplo vontade.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top