Pregunta

Tengo un valor de double f y me gustaría una forma de empujarlo ligeramente más grande (o más pequeño) para obtener un nuevo valor que sea lo más cercano posible al original pero aún estrictamente mayor que (o menos que) el original.

No tiene que estar cerca del último bit, es más importante que cualquier cambio que haga esté garantizado para producir un valor diferente y no redondear al original.

¿Fue útil?

Solución

Comprueba tu archivo math.h Si tiene suerte, tiene el nextafter y nextafterf funciones definidas. Hacen exactamente lo que usted quiere de manera portátil e independiente de la plataforma y son parte del estándar C99.

Otra forma de hacerlo (podría ser una solución alternativa) es descomponer su flotante en la mantisa y la parte del exponente. Incrementar es fácil: solo agrega uno a la mantisa. Si obtienes un desbordamiento, debes manejar esto incrementando tu exponente. La disminución funciona de la misma manera.

EDIT : Como se señaló en los comentarios, es suficiente para simplemente incrementar el flotador en su representación binaria. El exceso de mantisa incrementará el exponente, y eso es exactamente lo que queremos.

En pocas palabras, lo mismo que nextafter hace.

Aunque esto no será completamente portátil. Tendría que lidiar con la endianess y el hecho de que no todas las máquinas tienen flotadores IEEE (bueno, la última razón es más académica).

También el manejo de NAN e infinitos puede ser un poco complicado. No puedes simplemente incrementarlos como son, por definición, no como números.

Otros consejos

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

Sí, en serio.

Editar: Como alguien señaló, esto no se ocupa de los números -ve, Inf, Nan o desbordamiento correctamente. Una versión más segura de lo anterior es

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

En términos absolutos, la cantidad más pequeña que puede agregar a un valor de punto flotante para hacer un nuevo valor distinto dependerá de la magnitud actual del valor; será el tipo epsilon máquina multiplicado por el exponente actual.

Consulte la especificación IEEE para obtener información sobre puntos flotantes. La forma más sencilla sería reinterpretar el valor como un tipo entero, agregar 1, luego verificar (si le importa) que no haya volteado el signo o generado un NaN examinando los bits de signo y exponente.

Alternativamente, puedes usar frexp para obtener la mantissa actual y exponente, y por lo tanto calcular un valor para agregar.

Necesitaba hacer exactamente lo mismo y se me ocurrió 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;
}

Para lo que vale, el valor por el cual deja de funcionar el estándar ++ ++ es 9,007,199,254,740,992.

Puede que esto no sea exactamente lo que desea, pero aún así puede encontrar numeric_limits en uso. Particularmente los miembros min () y epsilon ().

No creo que algo como mydouble + numeric_limits :: epsilon () haga lo que quieras, a menos que mydouble ya esté cerca de epsilon. Si es así, entonces estás de suerte.

Hace tiempo que encontré este código, tal vez te ayude a determinar hasta qué punto puedes subirlo, incrementándolo por ese valor. Desafortunadamente no puedo recordar la referencia 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 bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top