Pregunta

En C89, floor () devuelve un doble. ¿Se garantiza que funcione lo siguiente?

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

Mi preocupación es que el resultado del piso podría no ser exactamente representable en IEEE 754. Entonces d obtiene algo así como 2.99999, y x termina siendo 2.

Para que la respuesta a esta pregunta sea sí, todos los enteros dentro del rango de un int deben ser exactamente representables como dobles, y el piso siempre debe devolver ese valor exactamente representado.

¿Fue útil?

Solución

Todos los enteros pueden tener una representación exacta de coma flotante si su tipo de coma flotante admite los bits de mantisa necesarios. Como double usa 53 bits para mantissa, puede almacenar todos los int de 32 bits exactamente. Después de todo, podría establecer el valor como mantisa con exponente cero.

Otros consejos

Si el resultado de floor () no es exactamente representable, ¿cuál espera que sea el valor de d? Seguramente si tiene la representación de un número de coma flotante en una variable, entonces, por definición, es exactamente representable, ¿no? tienes la representación en d ...

(Además, la respuesta de Mehrdad es correcta para entradas de 32 bits. En un compilador con un doble de 64 bits y un 64 bits int, tienes más problemas, por supuesto ...)

EDITAR: Quizás quisiste decir "el resultado teórico de floor (), es decir, el valor entero más grande menor o igual que el argumento, puede no ser representable como un int". Eso es ciertamente cierto. Una forma simple de mostrar esto para un sistema donde int es de 32 bits:

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

No recuerdo cómo fue C cuando las conversiones de punto flotante a desbordamiento de enteros ... pero sucederá aquí.

EDITAR: También hay otras situaciones interesantes a considerar. Aquí hay algunos códigos y resultados de C #: me imagino que al menos cosas similares sucederían en C. En C #, double se define como 64 bits y también lo es largo .

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

Resultados:

  

Valor original: 4611686018427387903
  Convertido a doble:   4.61168601842739E + 18
Suelos (como doble): 4.61168601842739E + 18
  Convertido de nuevo a largo:   4611686018427387904
  
  Valor original: 9223372036854775805
  Convertido a doble:   9.22337203685478E + 18
Suelos (como doble): 9.22337203685478E + 18
  Convertido de nuevo a largo:   -9223372036854775808

En otras palabras:

(long) floor((double) original)

no siempre es lo mismo que original . Esto no debería sorprendernos: hay más valores largos que dobles (dados los valores de NaN) y muchos dobles no son enteros, por lo que no podemos esperar que cada uno sea exactamente representable. Sin embargo, todos los enteros de 32 bits son representables como dobles.

Creo que estás un poco confundido acerca de lo que quieres preguntar. floor (3 + 0.5) no es un muy buen ejemplo, porque 3, 0.5 y su suma son exactamente representables en cualquier formato de punto flotante del mundo real. floor (0.1 + 0.9) sería un mejor ejemplo, y la verdadera pregunta aquí no es si el resultado de floor es exactamente representable, sino si la inexactitud de los números antes de llamar a floor dará como resultado un valor de retorno diferente al que esperaría, si todos los números fueran exactos. En este caso, creo que la respuesta es sí, pero depende mucho de sus números particulares.

Invito a otros a criticar este enfoque si es malo, pero una posible solución podría ser multiplicar su número por (1.0 + 0x1p-52) o algo similar antes de llamar a floor (quizás sería mejor usar nextafter ). Esto podría compensar los casos en los que un error en el último lugar binario del número hace que caiga justo debajo de un valor entero en lugar de exactamente, pero no tendrá en cuenta los errores que se han acumulado en varias operaciones. Si necesita ese nivel de estabilidad / exactitud numérica, debe hacer un análisis profundo o utilizar una biblioteca de precisión arbitraria o matemática exacta que pueda manejar sus números correctamente.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top