2016-05-02 1 views
0

J'ai trouvé quelque chose d'étrange.pow() comportement étrange comportement lorsqu'il est utilisé entre parenthèses et avec long entier entier

Cette fonction place un chiffre dans un nombre à l'endroit donné et renvoie le nombre modifié. Maintenant, nous voulons faire put_digit (123456789123456784, 0, 9); Cela mettra 9 à la fin du nombre, en remplaçant le dernier nombre (4).

Voici le code qui fonctionne:

long long int put_digit(long long int number, char place, char digit) 
{ 
    long long int result = number; 
    long long int power = number; 

    power = pow(10, (place)); 
    result -= get_digit(number, place)*power; 
    result += digit*pow(10, (place)); 

    return result; 
} 

Le code retourne 123456789123456789

Ceci est le code qui ne fonctionne pas:

long long int put_digit(long long int number, char place, char digit) 
{ 
    long long int result = number; 

    result -= get_digit(number, place)*pow(10, (place)); 
    result += digit*pow(10, (place)); 

    return result; 
} 

Ce code renvoie 123456789123456800 comme résultat.

Les fonctions get_digit() renvoie le chiffre du nombre à l'endroit donné. Ce est son code:

char get_digit(long long int number, char place) 
{ 
    long long int target = number; 
    char digit = 0; 

    target /= pow(10, place); 
    digit = target % 10; 

    return digit; 
} 

• Cela ne se produit pas avec des chiffres inférieurs.

• get_digit() renvoie toujours la valeur correcte (4 dans ce cas). • get_digit() est un caractère car il ne s'agit pas d'une fonction de compteur, il est donc préférable de se concentrer sur l'utilisation de moins de mémoire plutôt que d'utiliser une variable plus rapide comme int.

• J'ai essayé d'utiliser des parenthèses pour éviter la priorité gênante de l'opérateur, mais en vain.

• Un comportement bizarre est également observé lors de la commande put_digit (123456789123456000, 2, 7), qui renvoie 123456789123456704 pour une raison quelconque. Cette fonction est remplacée par la variable "power". Je ne comprends tout simplement pas pourquoi cela se produit.

Est-ce que je reçois un débordement? Est-ce la faute de mon système ou la mienne? Est-ce que j'utilise pow() dans un mauvais sens?

+1

C11 projet n1570 standard, * 6.3.1.8 conversions arithmétiques habituelles 1 [...] Dans le cas contraire , si le type réel correspondant de l'un ou l'autre opérande est double, l'autre opérande est converti, sans changement de type domaine, en un type dont le type réel correspondant est double. * – EOF

+0

Bon, corrigez-moi si je me trompe, mais le long nombre entier est converti en double jusqu'à ce que le calcul soit fait, puis subit une perte de précision en raison du grand nombre lorsqu'il est reconverti et affecté à la variable? –

+0

Si vous attribuez 'long long x = pow (y, z);', le résultat de 'pow()', qui est de type 'double', est converti en' long long' par troncature. Si vous multipliez 'get_digit() * pow()', le résultat de 'get_digit()', qui est un 'char', est converti en' double'. De plus, l'assignation composée 'long long x - = (double) foo' convertit' x' en double, fait la soustraction et convertit le résultat en 'long long'. Pourquoi vous vous attendez à ce que le résultat soit le même est au-delà de moi. – EOF

Répondre

4

La déclaration de pow() est: double pow(double base, double exponent);

Dans le premier cas:

long long int power = number; 

power = pow(10, (place)); 

la valeur retournée par pow() est converti en long long int quand il est attribué power. Le reste du calcul est traité en utilisant des nombres entiers et le résultat est celui que vous attendez.

Sur le second cas:

result -= get_digit(number, place)*pow(10, (place)); 

la valeur retournée par get_digit(number, place) est converti en double, car il doit être multiplié par un nombre à virgule flottante (retourné par pow()). En outre, la valeur result est convertie en double avant de soustraire le résultat de la multiplication.À la fin, la valeur calculée est convertie de double à long long int pour être stockée dans result. Mais à partir d'une certaine amplitude, les nombres à virgule flottante perdent la précision de leur (s) chiffre (s) le (x) moins significatif (s).

Essayez ce simple morceau de code pour voir par vous-même:

long long int i = 123456789123456785; 

for (; i <= 123456789123456795; i ++) { 
    printf("long long int: %lld; double: %f\n", i, (double)i); 
} 

Il produit:

long long int: 123456789123456785; double: 123456789123456784.000000 
long long int: 123456789123456786; double: 123456789123456784.000000 
long long int: 123456789123456787; double: 123456789123456784.000000 
long long int: 123456789123456788; double: 123456789123456784.000000 
long long int: 123456789123456789; double: 123456789123456784.000000 
long long int: 123456789123456790; double: 123456789123456784.000000 
long long int: 123456789123456791; double: 123456789123456784.000000 
long long int: 123456789123456792; double: 123456789123456800.000000 
long long int: 123456789123456793; double: 123456789123456800.000000 
long long int: 123456789123456794; double: 123456789123456800.000000 
long long int: 123456789123456795; double: 123456789123456800.000000 

Ce comportement est pas un bug mais une limitation des nombres à virgule flottante.

La solution pour votre code est de convertir la valeur retournée par pow(10, place)-long long int dès qu'il retourne:

result -= get_digit(number, place)*(long long int)pow(10, place); 
+4

Ou encore mieux, n'utilisez pas pow() 'sur les entiers. Il n'y a aucune garantie que, par exemple, 'pow (10.0 .10.0)' est exactement '1.0e10'. Il est assez facile d'écrire votre propre fonction "pow" qui fonctionne sur des entiers. –

+0

J'ai vu que pow() retourne un double, mais je n'ai jamais pensé que mon long long serait converti en double pour le calcul, je pensais que le résultat de pow() serait converti en un entier. Je suppose que l'écriture de ma propre fonction de pow pourrait vraiment être la voie à suivre. Merci. Encore une chose: Si je déclare int int power; et faire puissance = (longue int) pow (10, (lieu)); Je reçois "-2147483648" Est-ce parce que la variable de puissance déborde? Parce que je faisais ça en premier lieu et je n'ai pas pensé à utiliser longtemps comme dans l'exemple que j'ai donné. –