Pourquoi la division entière en C # renvoie-t-elle un entier et non un flottant?

131

Est-ce que quelqu'un sait pourquoi la division entière en C # renvoie un entier et non un flottant? Quelle est l'idée derrière cela? (Est-ce seulement un héritage de C / C ++?)

En C #:

float x = 13 / 4;   
//== operator is overridden here to use epsilon compare
if (x == 3.0)
   print 'Hello world';

Le résultat de ce code serait:

'Hello world'

À proprement parler, il n'y a pas de division entière (la division par définition est une opération qui produit un nombre rationnel, les entiers en étant un très petit sous-ensemble).

BanditoBunny
la source
46
parce que c'est integerdivision pas floating pointdivision.
Hunter McMillen
il doit (dans VB.Net) il est implémenté différemment d'une manière mathématique naturelle où tout le résultat de l'opération de division est un nombre irrationnel.
BanditoBunny
3
Je pense que vous voulez dire des nombres rationnels . Voir wikipedia : La division de deux entiers peut entraîner un reste. Pour compléter la division du reste, le système numérique est étendu pour inclure des fractions ou des nombres rationnels comme on les appelle plus généralement.
crashmstr
6
C'est la raison pour laquelle je ne suis pas fan de la "copie de syntaxe" dans les langues. Je viens de VB en pensant que "C # est .NET" et non "C # est comme C". Mon erreur je suppose, mais dans ce cas, je préfère la voie VB. S'ils ont eu la peine de générer une erreur de compilateur lors de l'utilisation de types simples non initialisés (vous n'obtenez même pas d'avertissement en C), alors pourquoi ne pas vous avertir lorsque vous affectez une division entière à un flottant?
darda

Réponses:

96

Bien qu'il soit courant pour les nouveaux programmeurs de commettre cette erreur en effectuant une division entière alors qu'ils voulaient réellement utiliser la division en virgule flottante, en pratique, la division entière est une opération très courante. Si vous supposez que les gens l'utilisent rarement et que chaque fois que vous effectuez une division, vous devez toujours vous rappeler de lancer des virgules flottantes, vous vous trompez.

Tout d'abord, la division entière est un peu plus rapide, donc si vous n'avez besoin que d'un résultat entier, vous voudrez utiliser l'algorithme le plus efficace.

Deuxièmement, il existe un certain nombre d'algorithmes qui utilisent la division entière, et si le résultat de la division était toujours un nombre à virgule flottante, vous seriez obligé d'arrondir le résultat à chaque fois. Un exemple du haut de ma tête est de changer la base d'un nombre. Le calcul de chaque chiffre implique la division entière d'un nombre avec le reste, plutôt que la division en virgule flottante du nombre.

Pour ces raisons (et pour d'autres), la division entière donne un entier. Si vous voulez obtenir la division en virgule flottante de deux entiers, vous devrez simplement vous rappeler d'en convertir un en double/ float/ decimal.

Servy
la source
5
Dans VB.Net, les architectes ont pris une autre décision: / - toujours une division flottante, \ - une division entière, donc c'est un peu incohérent, sauf si vous considérez l'héritage C ++;
BanditoBunny
4
Vous pouvez déterminer, au moment de la compilation, si l' /opérateur effectuera une division entière ou en virgule flottante (sauf si vous utilisez dynamique). S'il est difficile pour vous de le comprendre parce que vous faites tant sur cette ligne un, alors je vous suggère de briser cette ligne en plusieurs lignes de sorte que son plus facile à comprendre si les opérandes sont des entiers ou des types de virgule flottante. Les futurs lecteurs de votre code l'apprécieront probablement.
Servy
5
Personnellement, je trouve problématique que je doive toujours penser quelles sont les variables que je divise, je considère cela comme un gaspillage de mon attention.
BanditoBunny
8
@pelesl Comme ce serait un énorme changement de rupture de faire ce pour lequel un nombre astronomique de programmes serait cassé, je peux dire avec une totale confiance que cela ne se produira jamais en C #. C'est le genre de chose qui doit être faite dès le premier jour dans une langue ou pas du tout.
Servy
2
@Servy: Il y a beaucoup de choses comme ça en C, C ++ et C #. Personnellement, je pense que C # serait un meilleur langage s'il y avait eu un opérateur différent pour la division entière et, pour éviter que du code légitime ne donne un comportement étonnant, l' int/intopérateur était tout simplement illégal [avec un diagnostic spécifiant que le code doit convertir un opérande ou utiliser le autre opérateur, selon le comportement souhaité]. Si une autre bonne séquence de jetons était disponible pour la division d'entiers, il serait peut-être possible de déconseiller l'utilisation de /à cette fin, mais je ne sais pas ce qui serait pratique.
supercat du
77

Voir la spécification C # . Il existe trois types d'opérateurs de division

  • Division entière
  • Division en virgule flottante
  • Division décimale

Dans votre cas, nous avons la division Integer, avec les règles suivantes appliquées:

La division arrondit le résultat vers zéro et la valeur absolue du résultat est le plus grand entier possible qui est inférieur à la valeur absolue du quotient des deux opérandes. Le résultat est zéro ou positif lorsque les deux opérandes ont le même signe et zéro ou négatif lorsque les deux opérandes ont des signes opposés.

Je pense que la raison pour laquelle C # utilise ce type de division pour les entiers (certains langages renvoient un résultat flottant) est matérielle - la division des entiers est plus rapide et plus simple.

Sergey Berezovskiy
la source
Quelles langues renvoient un résultat flottant? @SergeyBerezovskiy
Ilaria
40

Chaque type de données est capable de surcharger chaque opérateur. Si le numérateur et le dénominateur sont des entiers, le type entier effectuera l'opération de division et retournera un type entier. Si vous souhaitez une division en virgule flottante, vous devez convertir un ou plusieurs nombres en types à virgule flottante avant de les diviser. Par exemple:

int x = 13;
int y = 4;
float x = (float)y / (float)z;

ou, si vous utilisez des littéraux:

float x = 13f / 4f;

Gardez à l'esprit que les points flottants ne sont pas précis. Si vous vous souciez de la précision, utilisez plutôt quelque chose comme le type décimal.

Steven Doggart
la source
1
+1 pour mentionner qu'un seul terme doit être flottant pour faire une division en virgule flottante.
Xynariz
De toute évidence, votre déclaration sur la précision est la bonne dans le contexte de l'apprentissage et de la rendre pas trop compliquée à comprendre. Comme nous devons être aussi précis que possible dans nos travaux, je veux encore clarifier la précision: selon IEE 754-1985, vous POUVEZ obtenir un résultat exact (même si ce n'est généralement pas le cas). Vous pouvez obtenir un résultat précis, lorsque les valeurs de calcul ont été présentées exactement auparavant et que le résultat est - pour parler simplement - une somme de puissances de 2. Même s'il n'est pas recommandé de s'appuyer sur cette précision dans ces cas particuliers.
L. Monty
Ajout sur la précision: la possibilité d'obtenir un résultat exact s'améliore considérablement car le résultat est proche de 1 ou -1. Il peut être un peu déroutant que cette propabilité reste toujours 0 car il existe des nombres infinis et un nombre fini de résultats, qui peuvent être présentés exactement. :)
L. Monty
1
@ L.Monty merci d'avoir soulevé cela. J'ai appris plus sur les virgules flottantes depuis la rédaction de cette réponse et le point que vous faites est juste. Techniquement, je dirais quand même que mon affirmation «les virgules flottantes ne sont pas précises» est acceptable, en ce sens que ce n'est pas parce que quelque chose peut être précis parfois que, dans son ensemble, il est précis. Comme on dit, une horloge cassée a raison deux fois par jour, mais je ne la qualifierais jamais d'instrument de précision. Je suis en fait plutôt surpris que ce soit la partie qui vous a plus dérangé que ma suggestion selon laquelle le type décimal est précis.
Steven Doggart
Les décimales sont imprécises, pour toutes les mêmes raisons que les flottants; c'est juste que les flottants sont en base 2 et les décimales en base 10. Par exemple, un type décimal ne peut pas contenir avec précision la valeur précise de 1/3.
Steven Doggart
11

Puisque vous n'utilisez aucun suffixe, les littéraux 13et 4sont interprétés comme un entier:

Manuel :

Si le littéral n'a pas de suffixe, il a le premier de ces types dans lesquels sa valeur peut être représentée: int, uint, long, ulong.

Ainsi, puisque vous déclarez 13comme entier, la division entière sera effectuée:

Manuel :

Pour une opération de la forme x / y, la résolution de surcharge d'opérateur binaire est appliquée pour sélectionner une implémentation d'opérateur spécifique. Les opérandes sont convertis en types de paramètres de l'opérateur sélectionné et le type du résultat est le type de retour de l'opérateur.

Les opérateurs de division prédéfinis sont répertoriés ci-dessous. Les opérateurs calculent tous le quotient de x et y.

Division entière:

int operator /(int x, int y);
uint operator /(uint x, uint y);
long operator /(long x, long y);
ulong operator /(ulong x, ulong y);

Et donc l'arrondissement se produit:

La division arrondit le résultat vers zéro et la valeur absolue du résultat est le plus grand entier possible qui est inférieur à la valeur absolue du quotient des deux opérandes. Le résultat est zéro ou positif lorsque les deux opérandes ont le même signe et zéro ou négatif lorsque les deux opérandes ont des signes opposés.

Si vous procédez comme suit:

int x = 13f / 4f;

Vous recevrez une erreur du compilateur, car une division en virgule flottante (l' /opérateur de 13f) entraîne un flottant, qui ne peut pas être converti implicitement en int.

Si vous voulez que la division soit une division à virgule flottante, vous devrez faire du résultat un flottant:

float x = 13 / 4;

Notez que vous diviserez toujours des entiers, qui seront implicitement convertis en float: le résultat sera 3.0. Pour déclarer explicitement les opérandes comme float, en utilisant le fsuffixe ( 13f, 4f).

CodeCaster
la source
+1 pour expliquer que vous pouvez avoir la réponse sous forme de flottant mais toujours faire une division entière. De plus, une autre façon courante que j'ai vue de forcer la division en virgule flottante est de multiplier le premier terme de la division par 1.0.
Xynariz
8

C'est juste une opération de base .

Souvenez-vous quand vous avez appris à diviser. Au début, nous avons résolu 9/6 = 1 with remainder 3.

9 / 6 == 1  //true
9 % 6 == 3 // true

L'opérateur / en combinaison avec l'opérateur% est utilisé pour récupérer ces valeurs.

L. Monty
la source
6

Cela pourrait être utile:

double a = 5.0/2.0;   
Console.WriteLine (a);      // 2.5

double b = 5/2;   
Console.WriteLine (b);      // 2

int c = 5/2;   
Console.WriteLine (c);      // 2

double d = 5f/2f;   
Console.WriteLine (d);      // 2.5
éozten
la source
Veuillez essayer d'ajouter quelques explications à votre réponse
NetStarter
La dernière expression produira 2.5, non 2.
Lasse V.Karlsen
Oui, mal orthographié. Merci.
eozten
4

Le résultat sera toujours du type qui a la plus grande plage du numérateur et du dénominateur. Les exceptions sont byte et short, qui produisent int (Int32).

var a = (byte)5 / (byte)2;  // 2 (Int32)
var b = (short)5 / (byte)2; // 2 (Int32)
var c = 5 / 2;              // 2 (Int32)
var d = 5 / 2U;             // 2 (UInt32)
var e = 5L / 2U;            // 2 (Int64)
var f = 5L / 2UL;           // 2 (UInt64)
var g = 5F / 2UL;           // 2.5 (Single/float)
var h = 5F / 2D;            // 2.5 (Double)
var i = 5.0 / 2F;           // 2.5 (Double)
var j = 5M / 2;             // 2.5 (Decimal)
var k = 5M / 2F;            // Not allowed

Il n'y a pas de conversion implicite entre les types à virgule flottante et le type décimal, donc la division entre eux n'est pas autorisée. Vous devez explicitement effectuer un cast et décider lequel vous voulez (Decimal a plus de précision et une plage plus petite que les types à virgule flottante).

zm
la source