2017-06-12 2 views
-5

Comment fonctionne réellement (floor) en C? Selon techonthenet. com,Comment fonctionne (sol) en C? Étrange résultat avec var = 4.2 lors de la multiplication de 100 et de l'appel (floor)

Dans la langue de programmation C, la fonction retourne étage le plus grand entier qui est inférieur ou égal à x (ex: bas arrondit le entier le plus proche).

Après avoir entré la valeur 4.2 dans get_float(), j'utilise étage (4,2 * 100), pour les transformer en cents et enlever décimales et d'en faire un entier.

Cependant, je suis perplexe pourquoi entrer la valeur spécifiquement 4.2 retournera une valeur différente pour l'étage (4.2 * 100) et le plancher (4.2 * 1000/10)? Cela a-t-il quelque chose à voir avec l'imprécision?

Voir l'image: When variable entered as 4.2, floor produces different values

BTW, je suis tout à fait nouveau à ce juste commencé CS50 sur EDX, et essayer le dernier exercice de la leçon de la semaine 1 .. aimerait également des commentaires sur d'autres parties du code . Plus d'informations sur le problème ici: http://docs.cs50.net/problems/greedy/greedy.html

+0

* Cela a-t-il quelque chose à voir avec l'imprécision * - oui. –

+1

Les questions demandant l'aide au débogage ("pourquoi ce code ne fonctionne-t-il pas?") Doivent inclure le comportement souhaité, un problème ou une erreur spécifique et le code le plus court nécessaire pour le reproduire dans la question elle-même. Les questions sans énoncé de problème clair ne sont pas utiles aux autres lecteurs. Voir: Comment créer un exemple minimal, complet et vérifiable. – Olaf

+0

Exercice auxiliaire: écrivez «1/3» comme un nombre décimal puis multipliez le nombre décimal avec «3». Avez-vous eu exactement '1'? Ne triche pas! Déterminez combien est floor (3 * 1/3) 'en utilisant les valeurs que vous venez de calculer. Une chose semblable se produit dans les ordinateurs pour tous les nombres qui ne peuvent pas être écrits comme 'm/n' avec' n' étant une puissance intégrale positive de '2'. – axiac

Répondre

0

Imprimez la valeur en dollars pour voir ce qui est réellement stocké avant d'essayer de définir la valeur.

1

float/double ne peut pas représenter exactement tous les nombres possibles. float typique peut représenter environ 2 différents nombres. 4.2 n'est pas l'un d'entre eux. Au lieu de cela, la valeur la plus proche est utilisée. Imprimer using FLT_DECIMAL_DIG voir assez de précision du nombre de savoir qu'il est pas exactement 4,2

#include <float.h> 
float a = 4.2f; 
printf("%.*e\n", FLT_DECIMAL_DIG-1, a); 
// 4.19999981e+08 

a *= 100; 
printf("%.*e\n", FLT_DECIMAL_DIG-1, a); 
// 4.19999969e+02 

a = floor(a); 
printf("%.*e\n", FLT_DECIMAL_DIG-1, a); 
// 4.19000000e+02 

Avec a*100 et a*1000 les produits arrondi à la encourir réponse et rappel repesentrable le plus proche, a n'est pas exactement 4.2.

a = 4.2; 
printf("%.*e\n", FLT_DECIMAL_DIG-1, a * 100);  // 4.19999969e+02 
printf("%f\n", floor(a * 100));     // 419.000000 
printf("%.*e\n", FLT_DECIMAL_DIG-1, a * 1000); // 4.20000000e+03 
printf("%.*e\n", FLT_DECIMAL_DIG-1, a * 1000/10); // 4.20000000e+02 
printf("%f\n", floor(a * 1000/10));    // 420.000000 

L'utilisation floor() est un mauvais choix de séparer 4,2 dans un nombre entier et fraction lorsque le nombre doit être arrondi au 0,01 le plus proche. Au lieu de cela, mettre à l'échelle, arrondir et ensuite séparer en utilisant des fonctions de type correspondant.

float a100 = a*100; 
    printf("%.*e\n", FLT_DECIMAL_DIG-1, a100); // 4.19999969e+02 
    a100 = roundf(a100); 
    printf("%.*e\n", FLT_DECIMAL_DIG-1, a100); // 4.20000000e+02 
    float a100th = fmodf(a100, 100); 
    printf("%.*e\n", FLT_DECIMAL_DIG-1, a100th); // 2.00000000e+01 
    a = (a100 - a100th)/100; 
    printf("%.*e\n", FLT_DECIMAL_DIG-1, a);  // 4.00000000e+00 

argent a beaucoup de préoccupations particulières en C et diverses approches ont leur weaknesses. L'utilisation de float est un choix très faible.

1

Cela a-t-il quelque chose à voir avec l'imprécision?

Cela a tout à voir avec l'imprécision.

Vous ne pouvez pas représenter un nombre infini de valeurs à virgule flottante avec un nombre fini de bits. Par conséquent, ce qui est réellement stocké dans un type à virgule flottante est une approximative pour la plupart des valeurs. 4.2 ne peut pas être représenté exactement dans un type 32 bits float; ce qui est réellement stocké est quelque chose de plus proche de 4.199999809. Vous avez besoin de 48 bits dans le significand pour le représenter exactement, ce qui signifie que vous devez utiliser un double 64 bits.

= sigh = Je ne peux pas ajouter. double vous obtient un beaucoup plus proche de 4,2, mais pas exactement.

Et ce n'est qu'une question de stockage, pas d'arrondi et de propagation d'erreur dans les opérations arithmétiques.

En général, il est préférable d'utiliser double au lieu de float pour le travail en virgule flottante (plus grande gamme et précision). En outre, il est préférable de stocker les montants en devise dans les types intégraux, mis à l'échelle de la plus petite unité (par exemple, au lieu de stocker 4.2 dollars, stocker 420 cents, ou 4200 mils). Vous aurez toujours besoin d'utiliser des types à virgule flottante pour calculer l'intérêt et des choses comme ça, mais vous voudrez stocker ces résultats à un type intégral après avoir fait l'arrondi et la mise à l'échelle appropriés.

+0

"Vous avez besoin de 48 bits dans le significand pour le représenter exactement, ... utiliser un double de 64 bits" -> 4.2 ne peut pas être représenté exactement comme un double binaire FP non plus. Demander des éclaircissements sur pourquoi 48 et son application ici. – chux

+0

@chux - Je ne peux pas ajouter. Frappé le passage offensant. –

+0

Beaucoup mieux sans le "48". Bonne et correcte utilisation de «mils» rares. Trivia: diverses unités d'argent des États-Unis: mil, cent, dime, dollar, aigle. – chux