Question

J'ai une double valeur f et je voudrais un moyen de la pousser légèrement plus grande (ou plus petite) pour obtenir une nouvelle valeur aussi proche que possible. à l'original mais toujours strictement supérieur (ou inférieur) à l'original.

Cela ne doit pas forcément être proche du dernier élément. Il est plus important que tout changement que je fais soit garanti de produire une valeur différente et non de revenir à l'original.

Était-ce utile?

La solution

Vérifiez votre fichier math.h. Si vous avez de la chance, vous avez le nextafter et nextafterf fonctions définies. Ils font exactement ce que vous voulez de manière portable et indépendante de la plate-forme et font partie de la norme C99.

Une autre façon de le faire (ce qui pourrait être une solution de secours) consiste à décomposer votre flottant dans la partie mantisse et exposant. L'incrémentation est facile: il suffit d'ajouter un à la mantisse. Si vous obtenez un dépassement, vous devez gérer cela en incrémentant votre exposant. La décrémentation fonctionne de la même manière.

MODIFIER : comme indiqué dans les commentaires, il suffit d'incrémenter le flottant dans sa représentation binaire. Le dépassement de mantisse incrémentera l'exposant, et c'est exactement ce que nous voulons.

En résumé, c'est la même chose que ce que fait ensuite.

Cependant, cela ne sera pas complètement portable. Vous auriez à vous soucier de l’endurance et du fait que toutes les machines n’ont pas de flotteurs IEEE (ok - la dernière raison est plus académique).

Aussi, manipuler les NAN et les infinis peut être un peu délicat. Vous ne pouvez pas simplement les incrémenter comme ils sont par définition, pas des nombres.

Autres conseils

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

Oui, sérieusement.

Modifier: Comme quelqu'un l'a fait remarquer, cela ne traite pas correctement les nombres -ve, Inf, Nan ou le débordement. Une version plus sûre de ce qui précède est

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 termes absolus, la plus petite quantité que vous pouvez ajouter à une valeur à virgule flottante pour créer une nouvelle valeur distincte dépendra de la magnitude actuelle de la valeur; ce sera la machine epsilon du type multipliée par l'exposant actuel.

Consultez la spécification IEEE pour la représentation en virgule flottante. Le moyen le plus simple serait de réinterpréter la valeur en tant que type entier, d'ajouter 1, puis de vérifier (si vous le voulez bien) que vous n'avez pas retourné le signe ni généré de NaN en examinant les bits de signe et d'exposant.

Vous pouvez également utiliser frexp pour obtenir la mantisse actuelle. et exposant, et donc calculer une valeur à ajouter.

Je devais faire exactement la même chose et suis venu avec ce code:

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

Pour ce que cela vaut, la valeur pour laquelle l'incrémentation ++ ++ cesse de fonctionner est 9 007 199 254 740 992.

Cela peut ne pas être exactement ce que vous souhaitez, mais vous pouvez toujours trouver limites_numériques en cours d'utilisation. En particulier les membres min () et epsilon ().

Je ne crois pas que quelque chose comme mydouble + numeric_limits :: epsilon () fera ce que vous voulez, à moins que mydouble ne soit déjà proche de epsilon. Si c'est le cas, vous avez de la chance.

J'ai trouvé ce code il y a quelque temps. Cela vous aidera peut-être à déterminer le plus petit que vous puissiez augmenter, puis vous l'incrémentez de cette valeur. Malheureusement, je ne me souviens plus de la référence de ce code:

#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);
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top