2 votes

l'effet de la précision de la virgule flottante pour tester si une variable atteint une limite

J'ai une fonction C/C++ checkt (FLT t), où FLT est la précision en virgule flottante définie par le préprocesseur, que je veux utiliser dans un corps de code beaucoup plus important pour m'assurer que les valeurs qui lui sont passées sont dans la plage [0,1]. Il est souvent utilisé pour augmenter une variable par incréments fixes et déclencher une erreur lorsque l'argument est hors de la plage. Dans cet exemple simple, je veux passer de 0,0 à 1,0 en dix incréments de 0,1. Cependant, comme 0,1 n'est pas représenté parfaitement comme 0,1000000000 etc. jusqu'à la limite de la précision, j'atteindrai exactement la valeur 1,0 à la fin de la boucle. L'extrait de code ci-dessous fonctionne si FLT est réglé sur double mais pas s'il est réglé sur flottant. Je joins également la sortie des deux. Le compilateur est gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0.

#include <vector>
#include <iostream>

#define FLT double

void checkt (FLT t) {
    if (t < FLT{0} || t > FLT{1}) {
        std::cout << "t before error = " << t << std::endl;
        throw std::runtime_error ("t out of range [0,1]");
    }
}

int main(int argc, char **argv) {
    FLT x = FLT{0.0};
    FLT inc = FLT{0.1};
    FLT y = FLT{1};

    std::cout.precision(16);
    std::cout << "x is " << x << "y is " << y << " inc is " << inc << std::endl;

    for (int i=0; i<11; i++){ 
        std::cout.precision(16);
        std::cout << "x is " << x << std::endl;
        checkt(x);
        x += inc;
    }

    return 0;
}

Sortie avec FLT=double x est 0y est 1 inc est 0.1

x is 0
x is 0.1
x is 0.2
x is 0.3
x is 0.4
x is 0.5
x is 0.6
x is 0.7
x is 0.7999999999999999
x is 0.8999999999999999
x is 0.9999999999999999

La sortie avec FLT = float est

y is 1 inc is 0.1000000014901161
x is 0
x is 0.1000000014901161
x is 0.2000000029802322
x is 0.300000011920929
x is 0.4000000059604645
x is 0.5
x is 0.6000000238418579
x is 0.7000000476837158
x is 0.8000000715255737
x is 0.9000000953674316
x is 1.00000011920929
t before error = 1.00000011920929
terminate called after throwing an inst

2voto

dbush Points 8590

En raison de la nature imprécise de la virgule flottante binaire, l'addition répétée à chaque itération de la boucle peut entraîner l'accumulation d'un facteur d'erreur au fil du temps.

Plutôt que d'ajouter le montant de l'augmentation à x à chaque itération, multiplie l'incrément par l'indice de la boucle et définit x à cela.

for (int i=0; i<11; i++){
    x = inc * i;
    std::cout.precision(17);
    std::cout << "x is " << x << std::endl;
    checkt(x);
}

Sortie pour double :

x is 0y is 1 inc is 0.1
x is 0
x is 0.10000000000000001
x is 0.20000000000000001
x is 0.30000000000000004
x is 0.40000000000000002
x is 0.5
x is 0.60000000000000009
x is 0.70000000000000007
x is 0.80000000000000004
x is 0.90000000000000002
x is 1

Sortie pour float :

x is 0y is 1 inc is 0.1000000014901161
x is 0
x is 0.10000000149011612
x is 0.20000000298023224
x is 0.30000001192092896
x is 0.40000000596046448
x is 0.5
x is 0.60000002384185791
x is 0.69999998807907104
x is 0.80000001192092896
x is 0.90000003576278687
x is 1

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X