Question

Dans C89, floor () renvoie un double. Les éléments suivants sont-ils garantis pour fonctionner?

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

Je crains que le résultat de floor ne soit pas exactement représentable dans IEEE 754. Donc, d obtient quelque chose comme 2.99999 et x finit par être 2.

Pour que la réponse à cette question soit affirmative, tous les entiers compris dans l'intervalle d'un entier doivent être exactement représentables sous forme de doublons, et Floor doit toujours renvoyer la valeur représentée exactement.

Était-ce utile?

La solution

Tous les entiers peuvent avoir une représentation en virgule flottante exacte si votre type à virgule flottante prend en charge les bits de mantisse requis. Etant donné que double utilise 53 bits pour la mantisse, il peut stocker tous les int 32 bits de manière exacte. Après tout, vous pouvez simplement définir la valeur en tant que mantisse avec un exposant nul.

Autres conseils

Si le résultat de floor () n'est pas exactement représentable, quelle est, selon vous, la valeur de d? Si vous avez obtenu la représentation d’un nombre à virgule flottante dans une variable, alors, par définition, il est exactement représentable, n'est-ce pas? Vous avez obtenu la représentation dans d ...

(En outre, la réponse de Mehrdad est correcte pour les ints 32 bits. Dans un compilateur avec un double 64 bits et un un entier 64 bits, vous avez bien sûr plus de problèmes ...)

EDIT: Vous vouliez peut-être dire "le résultat théorique de floor (), c'est-à-dire la valeur entière la plus grande inférieure ou égale à l'argument, peut ne pas être représentable sous la forme d'un entier". C'est certainement vrai. Moyen simple d’afficher ceci pour un système où int est égal à 32 bits:

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

Je ne me souviens pas tout à fait de ce que fait C lorsque les conversions d'un nombre à virgule flottante à un dépassement d'entier ... mais cela se produira ici.

EDIT: Il y a d'autres situations intéressantes à considérer aussi. Voici quelques exemples de code C # et de résultats. J'imagine qu'au moins des choses similaires se produiraient en C. En C #, double est défini sur 64 bits, de même que 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();
    }
}

Résultats:

  

Valeur originale: 4611686018427387903
  Converti en double:   4.61168601842739E + 18
Plancher (double): 4.61168601842739E + 18
  Reconverti en long:   4611686018427387904
  
  Valeur originale: 9223372036854775805
  Converti en double:   9.22337203685478E + 18
Plancher (double): 9.22337203685478E + 18
  Reconverti en long:   -9223372036854775808

En d'autres termes:

(long) floor((double) original)

n'est pas toujours identique à original . Cela ne devrait pas nous surprendre - il existe plus de valeurs longues que de doubles (étant donné les valeurs de NaN) et de nombreux doubles ne sont pas des nombres entiers. Nous ne pouvons donc pas nous attendre à ce qu'ils soient tous exactement représentables. Cependant, tous les entiers 32 bits sont représentables en double.

Je pense que vous êtes un peu confus quant à ce que vous voulez demander. floor (3 + 0.5) n'est pas un très bon exemple, car 3, 0,5 et leur somme sont parfaitement représentables dans n'importe quel format à virgule flottante du monde réel. floor (0.1 + 0.9) en serait un meilleur exemple. La vraie question n'est pas de savoir si le résultat de floor est exactement représentable, mais si l'inexactitude des nombres avant d'appeler l'appel de floor donnera une valeur de retour différente de celle à laquelle vous vous attendiez, si tous les nombres avaient été exacts. Dans ce cas, je pense que la réponse est oui, mais cela dépend beaucoup de vos chiffres.

J'invite les autres à critiquer cette approche si elle est mauvaise, mais une solution de contournement possible consiste à multiplier votre nombre par (1.0 + 0x1p-52) ou quelque chose de similaire avant d'appeler floor (il serait peut-être préférable d'utiliser nextafter ). Cela pourrait compenser les cas où une erreur à la dernière place binaire du nombre le ferait tomber juste en dessous et non exactement sur une valeur entière, mais ne tiendrait pas compte des erreurs qui se seraient accumulées au cours de plusieurs opérations. Si vous avez besoin de ce niveau de stabilité / exactitude numérique, vous devez effectuer une analyse approfondie ou utiliser une bibliothèque de précision arbitraire ou mathématique exacte capable de gérer vos chiffres correctement.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top