Pergunta

eu tenho um double valor f e gostaria de uma maneira de cutucá -lo um pouco maior (ou menor) para obter um novo valor que estará o mais próximo possível do original, mas ainda estritamente maior que (ou menos que) o original.

Não precisa estar perto do último bit - é mais importante que qualquer mudança que eu faça é garantida produzir um valor diferente e não voltar ao original.

Foi útil?

Solução

Verifique seu arquivo math.h. Se você tiver sorte, você tem o nextafter e nextafterf funções definidas. Eles fazem exatamente o que você deseja de uma maneira portátil e de plataforma e fazem parte do padrão C99.

Outra maneira de fazê -lo (poderia ser uma solução de fallback) é decompor seu flutuador na Mantissa e na parte do expoente. Incrementação é fácil: basta adicionar um ao Mantissa. Se você receber um transbordamento, precisará lidar com isso incrementando seu expoente. Diminuir funciona da mesma maneira.

EDITAR: Como apontado nos comentários, é suficiente apenas incrementar o flutuador em sua representação binária. O Mantissa-Overflow aumentará o expoente, e é exatamente isso que queremos.

Isso é, em poucas palavras, a mesma coisa que Nextafter faz.

Isso não será completamente portátil. Você teria que lidar com a Endianess e o fato de que nem todas as máquinas têm flutuações IEEE (OK - a última razão é mais acadêmica).

Também lidar com nan e infinitos pode ser um pouco complicado. Você não pode simplesmente incrementá -los como eles são por definição, não números.

Outras dicas

u64 &x = *(u64*)(&f);
x++;

Sim seriamente.

Editar: Como alguém apontou, isso não lida com números -ve, inf, nan ou transbordamento corretamente. Uma versão mais segura do exposto é

u64 &x = *(u64*)(&f);
if( ((x>>52) & 2047) != 2047 )    //if exponent is all 1's then f is a nan or inf.
{
    x += f>0 ? 1 : -1;
}

Em termos absolutos, a menor quantidade que você pode adicionar a um valor de ponto flutuante para fazer um novo valor distinto dependerá da magnitude atual do valor; Será o tipo Máquina epsilon multiplicado pelo expoente atual.

Confira o IEEE Spec Para o ponto flutuante representar. A maneira mais simples seria reinterpretar o valor como um tipo de número inteiro, adicione 1 e verifique (se você se importa) se você não girou a placa ou gerou uma NAN examinando os bits de sinal e expoente.

Alternativamente, você pode usar Frexp Para obter o Mantissa e o expoente atuais e, portanto, calcule um valor a ser adicionado.

Eu precisava fazer exatamente a mesma coisa e inventar este código:

double DoubleIncrement(double value)
{
  int exponent;
  double mantissa = frexp(value, &exponent);
  if(mantissa == 0)
    return DBL_MIN;

  mantissa += DBL_EPSILON/2.0f;
  value = ldexp(mantissa, exponent);
  return value;
}

Pelo que vale, o valor para o qual o incremento padrão ++ deixa de funcionar é de 9.007.199.254.740.992.

Pode não ser exatamente o que você deseja, mas você ainda pode encontrar numeric_limits em uso. Particularmente os membros min () e epsilon ().

Não acredito que algo como mydouble + numeric_limits :: epsilon () fará o que você deseja, a menos que MyDouble já esteja perto de Epsilon. Se for, então você está com sorte.

Encontrei esse código há algum tempo, talvez o ajude a determinar o menor que você pode pressioná -lo, apenas aumentá -lo por esse valor. Infelizmente, não me lembro da referência para este código:

#include <stdio.h>

int main()
{
    /* two numbers to work with */
    double number1, number2;    // result of calculation
    double result;
    int counter;        // loop counter and accuracy check

    number1 = 1.0;
    number2 = 1.0;
    counter = 0;

    while (number1 + number2 != number1) {
        ++counter;
        number2 = number2 / 10;
    }
    printf("%2d digits accuracy in calculations\n", counter);

    number2 = 1.0;
    counter = 0;

    while (1) {
        result = number1 + number2;
        if (result == number1)
            break;
        ++counter;
        number2 = number2 / 10.0;
    }

    printf("%2d digits accuracy in storage\n", counter );

    return (0);
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top