Diffuser vers int vs sol

120

Y a-t-il une différence entre ceux-ci:

float foo1 = (int)(bar / 3.0);
float foo2 = floor(bar / 3.0);

Si je comprends bien, les deux cas ont le même résultat. Y a-t-il une différence dans le code compilé?

OgreSwamp
la source
1
un peu mieux avec floor, mais attention que ce n'est doublepas pour float. C99 a également floorfpour float.
Jens Gustedt
2
Donc, ils ont le même résultat tant que la barre est positive
Zac
1
(note: en C ++ s'il vous plaît #include<cmath>et utilisez std::floor)
user202729
Quel type est bar?
chux - Réintégrer Monica
@chux Peu importe, diviser par 3,0 le fera quand même doubler
kaalus

Réponses:

194

La conversion en un entier tronquera vers zéro. floor()tronquera vers l'infini négatif. Cela vous donnera des valeurs différentes si elles barétaient négatives.

James Curran
la source
15
Je pense que vous avez frappé dans le mille ici. Une autre différence, si floor()c'est l'intention, est si la valeur de barest trop grande pour tenir dans un int.
Fred Larson
Avez-vous une source pour cette déclaration?
HelloGoodbye
1
Même lorsque le résultat est positif, ce n'est pas garanti. Voyez ceci et cela .
user202729
27

Comme cela a été dit précédemment, pour les nombres positifs, ils sont identiques, mais ils diffèrent pour les nombres négatifs. La règle est que int arrondit vers 0, tandis que le plancher arrondit vers l'infini négatif.

floor(4.5) = (int)4.5 = 4
floor(-4.5) = -5 
(int)(-4.5) = -4

Cela étant dit, il existe également une différence de temps d'exécution. Sur mon système, j'ai chronométré que le casting est au moins 3 fois plus rapide que le sol.

J'ai du code qui nécessite le fonctionnement au sol d'une plage limitée de valeurs, y compris des nombres négatifs. Et il doit être très efficace, nous utilisons donc la fonction suivante pour cela:

int int_floor(double x) 
{ 
    return (int)(x+100000) - 100000; 
}

Bien sûr, cela échouera pour de très grandes valeurs de x (vous rencontrerez des problèmes de dépassement de capacité) et pour des valeurs négatives inférieures à -100000, etc. pour notre application. Prenez-le avec un grain de sel, testez-le sur votre système, etc. mais cela vaut la peine d'être considéré à mon humble avis.

Brice Rebsamen
la source
"Je l'ai chronométré pour être au moins 3 fois plus rapide que le sol" -> OP utilise float, non double- peut-être doubleétait votre application. Si en C, veillez à utiliser floorf()avec floats.
chux
@chux Je pense que la seule raison pour laquelle il y a une différence est que la distribution permet une optimisation au moment de la compilation. Cette conversion peut donc avoir été complètement supprimée lors de l'exécution.
ClydeTheGhost
9

SO 101, ne changez pas votre question une fois que les gens ont répondu à votre question, écrivez plutôt une nouvelle question.

Pourquoi pensez-vous qu'ils auront le même résultat?

float foo = (int)(bar / 3.0) //will create an integer then assign it to a float

float foo = fabs(bar / 3.0 ) //will do the absolute value of a float division

bar = 1.0

foo1 = 0;
foo2 = 0.33333...
AndersK
la source
1
Que voulez-vous dire fabs? La question portait sur floor. Le sol de 0.33333... est 0.
Aaron Franke
2
@AaronFranke la question d'origine a été modifiée. semble que beaucoup de choses peuvent se passer dans 8 ans ;-) remarquez que d'autres réponses ont le même principe
AndersK
4

EDIT: Parce que la question peut avoir été modifiée en raison de la confusion entre fabs()et floor().

Compte tenu des lignes d'exemple de question d'origine:

1.  float foo = (int)(bar / 3.0);

2.  float foo = fabs(bar / 3.0);

La différence est que si la barre est négative, le résultat sera négatif avec le premier mais positif avec le second. Le premier sera tronqué à un entier et le second renverra la valeur décimale complète, y compris la partie fractionnaire.

Amardeep AC9MF
la source
3

Oui. fabsrenvoie la valeur absolue de son argument, et le transtypage en int provoque la troncature de la division (jusqu'à l'entier le plus proche), de sorte que les résultats seront presque toujours différents.

warrenm
la source
2

Il existe deux différences principales:

  1. Comme d'autres l'ont souligné, la conversion en un entier tronquera vers zéro, alors que floor()sera toujours tronquée vers l'infini négatif; c'est un comportement différent pour un opérande négatif.

  2. Personne (encore) ne semble avoir signalé une autre différence - si votre argument est supérieur ou égal à MAX_INT+1(ou inférieur à -MAX_INT-1), alors la conversion en un intentraînera la suppression des bits les plus hauts (C, probablement) ou un comportement indéfini ( C ++ et éventuellement C). EG si votre intest de 32 bits, vous n'aurez qu'un bit de signe plus 31 bits de données. Donc, utiliser ceci avec un doublequi est de grande taille va produire des résultats inattendus.

abligh
la source
2.a. La condition exacte pour la conversion en intdébordement est que l'argument soit supérieur ou égal à INT_MAX+1. Symétriquement, la condition du dépassement inférieur est que l'argument soit inférieur ou égal à INT_MIN-1.
Pascal Cuoq
1
2.b. Le débordement dans la conversion de virgule flottante en nombre entier est un comportement non défini en C ++. Cela n'entraîne pas la suppression des bits les plus élevés. Voir (bien qu'il soit écrit pour C): blog.frama-c.com/index.php?post/2013/10/09/…
Pascal Cuoq
0

(int) xest une requête pour conserver la partie entière de x(il n'y a pas d'arrondi ici)

fabs(x)= | x | pour que ce soit >= 0;

Ex: (int) -3.5retourne -3; fabs(-3.5)retourne 3.5;

En général, fabs (x) >= xpour tout x;

x >= (int) x si x >= 0

x < (int) x si x < 0

Paul Hoang
la source
x = -3 fabs (-3) = 3 (entier) -3 = -3; Je pense que les dernières inégalités tiennent. Pouvez-vous expliquer plus en détail pourquoi c'est faux?
Paul Hoang
Désolé, je voulais dire -3,5, l'exemple que vous avez donné. -3> -3,5
Dennis Zickefoose
3
La dernière instruction doit toujours être "x <= int (x) if x <0", et non "x <(int) x if x <0": les entiers négatifs restent les mêmes.
Tomasz Gandor