¿Cómo alterar un flotador por su incremento más pequeño (o cerca de él)?

Tengo un double valor 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 parecido posible al original pero estrictamente mayor que (o menor) que el original.

No tiene que ser muy cercano al último bit; es más importante que cualquier cambio que realice garantice la producción de un valor diferente y no el retorno al original.

Verifica tu archivo math.h Si tiene suerte, tiene definidas las funciones nextafter y nextafterf . Hacen exactamente lo que quieren de una manera portátil y plataforma independiente y son parte del estándar C99.

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

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

Eso es en pocas palabras lo mismo que nunca después.

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

También manejar NAN e infinites puede ser un poco complicado. No puedes simplemente incrementarlos como son por definición no números.

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

Sí, en serio.

Editar: Como alguien señaló, esto no se trata de -ve números, 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 pueda agregar a un valor de punto flotante para hacer un nuevo valor distinto dependerá de la magnitud actual del valor; será el épsilon de máquina del tipo multiplicado por el exponente actual.

Consulte la especificación IEEE para la representación de coma flotante. La forma más simple sería reinterpretar el valor como un tipo entero, agregar 1, luego verificar (si le importa) que no ha volteado el signo o generado un NaN examinando los bits de signo y exponente.

Alternativamente, puede usar frexp para obtener la mantisa actual y el 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; } 

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

Puede que esto no sea exactamente lo que quiere, pero aún puede encontrar los límites numéricos 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 épsilon. Si es así, entonces estás de suerte.

Encontré este código hace un tiempo, tal vez te ayude a determinar lo más pequeño que puedes empujarlo para luego incrementarlo en ese valor. Lamentablemente, no puedo recordar la referencia de este código:

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