Pergunta

Em C89, floor () retorna um duplo. É o seguinte garantida ao trabalho?

double d = floor(3.0 + 0.5);
int x = (int) d;
assert(x == 3);

A minha preocupação é que o resultado de chão pode não ser exatamente representável em IEEE 754. Então d recebe algo como 2,99999, e x acaba sendo 2.

Para a resposta a esta pergunta é sim, todos os inteiros no intervalo de um int tem que ser exatamente representável como duplos e piso deve sempre retornar esse valor exatamente representados.

Foi útil?

Solução

Todos os inteiros pode ter representação ponto exato flutuante se o seu tipo de ponto flutuante suporta os bits mantissa necessários. Desde double usa 53 bits para mantissa, ele pode armazenar todos os ints 32 bits exatamente. Afinal de contas, você poderia apenas definir o valor como mantissa com expoente zero.

Outras dicas

Se o resultado de andar () não é exatamente representável, o que você espera o valor de d de ser? Certamente, se você tem a representação de um número de ponto flutuante em uma variável, então, por definição, é exatamente representável não é? Você tem tem a representação em d ...

(Além disso, a resposta de Mehrdad está correta para ints de 32 bits. Em um compilador com um pouco de 64 double e a 64 bit int, você tem mais problemas, claro ...)

EDIT: Talvez você quis dizer "o resultado teórico de chão (), ou seja, o maior valor inteiro menor ou igual ao argumento, pode não ser representável como um int". Isso é certamente verdade. maneira simples de mostrar isso para um sistema onde int é de 32 bits:

int max = 0x7fffffff;
double number = max;
number += 10.0;
double f = floor(number);
int oops = (int) f;

Não me lembro de improviso o C faz quando as conversões de ponto de estouro de inteiro flutuante ... mas isso vai acontecer aqui.

EDIT: Há outras situações interessantes a considerar também. Eis alguns resultados de código C # e -. Eu imagino que, pelo menos, semelhante coisas aconteceriam em C. Em C #, double é definido como 64 bits e assim é long

using System;
class Test
{
    static void Main()
    {
        FloorSameInteger(long.MaxValue/2);
        FloorSameInteger(long.MaxValue-2);
    }

    static void FloorSameInteger(long original)
    {
        double convertedToDouble = original;
        double flooredToDouble = Math.Floor(convertedToDouble);
        long flooredToLong = (long) flooredToDouble;

        Console.WriteLine("Original value: {0}", original);
        Console.WriteLine("Converted to double: {0}", convertedToDouble);
        Console.WriteLine("Floored (as double): {0}", flooredToDouble);
        Console.WriteLine("Converted back to long: {0}", flooredToLong);
        Console.WriteLine();
    }
}

Resultado:

valor original: 4611686018427387903
Convertidos para o dobro: 4.61168601842739E + 18
Floored (como duplo): 4.61168601842739E + 18
volta convertido para longa: 4611686018427387904

Valor original: 9223372036854775805
Convertidos para o dobro: 9.22337203685478E + 18
Floored (como duplo): 9.22337203685478E + 18
volta convertido para longa: -9223372036854775808

Em outras palavras:

(long) floor((double) original)

nem sempre é o mesmo que original. Isto não deve vir como nenhuma surpresa - há valores mais longos do que duplica (dada a valores NAN) e abundância de duplas não são números inteiros, por isso não podemos esperar que todos os tempo para ser exatamente representável. No entanto, todos os 32 bits inteiros são representável como duplos.

Eu acho que você está um pouco confuso sobre o que você quer perguntar. floor(3 + 0.5) não é um bom exemplo, porque 3, 0,5, e sua soma são exatamente representável em qualquer do mundo real formato de ponto flutuante. floor(0.1 + 0.9) seria um exemplo melhor, e a verdadeira questão aqui não é se o resultado de floor é exatamente representável, mas se inexatidão dos números antes chamando floor irá resultar em um valor de retorno diferente do que você seria de esperar, tinha todos os números sido exata. Neste caso, acredito que a resposta é sim, mas depende muito de seus números particulares.

Eu convido outros a criticar esta abordagem se é ruim, mas uma possível solução poderia ser a de multiplicar o seu número por (1.0+0x1p-52) ou algo semelhante antes de chamar floor (talvez usando nextafter seria melhor). Isto poderia compensar casos em que um erro no último lugar binária do número faz com que ele a cair logo abaixo, em vez de exatamente em um valor inteiro, mas não serão responsáveis ??por erros que se acumularam ao longo de um número de operações. Se você precisa que o nível de numérico de estabilidade / exatidão, você precisa se quer fazer alguma análise profunda ou usar um de precisão arbitrária exata ou de matemática biblioteca que pode lidar com seus números corretamente.

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