web-dev-qa-db-fra.com

C #: Comment faire des calculs simples, arrondis, sur des nombres entiers?

je veux le résultat d'une équation arrondie à l'entier le plus proche. par exemple.

137 * (3/4) = 103

Considérez le code incorrect suivant.

int width1 = 4;
int height1 = 3;

int width2 = 137;
int height2 = width2 * (height1 / width1);

Quelle est la bonne façon d'effectuer des calculs "entiers" en C #?

Dois-je vraiment faire:

int height2 = (int)Math.Round(
      (float)width2 * ((float)height1 / (float)width1)
   );
19
Ian Boyd

Comme indiqué ci-dessus, vous devriez faire la multiplication avant la division. De toute façon, vous pouvez écrire votre expression en ne lançant que le diviseur:

int height2 = (int)Math.Round(width2 * (height1 / (float)width1));
16
Aaron
int height2 = (width2 * height1) / width1;
3

Faites la multiplication en premier (tant qu’ils ne risquent pas de déborder) et la division en second.

3/4 == 0 en calcul mathématique entier (le calcul d'un calcul entier ne se termine pas par division, il est tronqué).

Si vous avez vraiment besoin d’arrondir, vous devez soit travailler en virgule fixe, virgule flottante ou jouer avec le module.

3
plinth

Je pense que c'est la manière la plus élégante de faire ceci:

Math.round(myinteger * 0.75);

Utiliser 0.75 au lieu de 3/4 le convertira implicitement en double/float et pourquoi ne pas utiliser les fonctions par défaut fournies à cet effet?

2
Ruben

Je suis juste tombé sur cette question. La réponse d'Aaron me semble presque juste. Mais je suis certain que vous devez spécifier une zone intermédiaire si votre problème est un problème du "monde réel". Donc, ma réponse, basée sur le code d'Aaron, est

int height2 = (int)Math.Round(width2 * (height1 / (float)width1),MidpointRounding.AwayFromZero);

Pour voir la différence, exécutez ce code dans la console

    Console.WriteLine((int)Math.Round(0.5));
    Console.WriteLine((int)Math.Round(1.5));
    Console.WriteLine((int)Math.Round(2.5));
    Console.WriteLine((int)Math.Round(3.5));
    Console.WriteLine((int)Math.Round(0.5, MidpointRounding.AwayFromZero));
    Console.WriteLine((int)Math.Round(1.5, MidpointRounding.AwayFromZero));
    Console.WriteLine((int)Math.Round(2.5, MidpointRounding.AwayFromZero));
    Console.WriteLine((int)Math.Round(3.5, MidpointRounding.AwayFromZero));
    Console.ReadLine();

Pour plus de détails, vous pouvez regarder this article.

1
Pianoman

Je corrigerai le code incorrect en ajoutant zéro lignes de code:

float width1 = 4;
float height1 = 3;

float width2 = 137;
float height2 = width2 * (height1 / width1);

Vous devez utiliser des éléments flottants pour les variables pouvant contenir des nombres décimaux. Cela inclut les hauteurs calculées à partir de ratios. Vous pouvez toujours utiliser cast plus tard si cela pose problème.

1
andrewrk

Jamais lancé pour flotter, il a même moins de précision qu'un entier 32 bits. Si vous utilisez une virgule flottante, utilisez toujours double au lieu de float.

1
rwallace

Six ans plus tard, voici ma contribution - un petit truc que j'ai appris il y a longtemps, et je suis étonné que personne d'autre ne l'ait mentionné ici.

L’idée est d’arrondir en ajoutant la moitié du diviseur au numérateur avant de procéder à la division. 

    int height2 = (width2 * height1 + width1 / 2) / width1;

En réalité, je ne recommanderais pas nécessairement de le faire dans les cas, comme ceux du PO, où le diviseur est une variable. Au lieu de cela, il peut être préférable d’utiliser Math.Round (), car il est beaucoup plus facile à comprendre.

Mais dans les cas où le diviseur est une constante, j'utilise cette astuce. Donc au lieu de 

    int height2 = width2 * height1 / 4;  // No rounding

J'utiliserais

    int height2 = (width2 * height1 + 2) / 4;

Voici un exemple plus typique

  private static Size ResizeWithPercentage(Size oldSize, int resizePercentage)
  {
     return new Size((oldSize.Width * resizePercentage + 50) / 100, 
                     (oldSize.Height * resizePercentage + 50) / 100);
  }

Une autre possibilité consiste à associer cette astuce à l’idée évoquée par dongilmore et supercat, selon laquelle au lieu d’avoir une division par deux spécifiée ou implicite, vous pouvez multiplier le numérateur et le dénominateur par deux.

    int height2 = (width2 * height1 * 2 + width1) / (width1 * 2);

Cela donne de meilleures réponses dans les cas où le diviseur est, ou peut être, un nombre impair.

1
RenniePet
int height2 = ((width2 * height1 * 10) + 5) / (width1 * 10);

Avant toute division entière, vérifiez que le diviseur est différent de 0. Notez également que cela suppose un quotient positif. Si négatif, l'arrondi doit être -5, pas +5. 

0
dongilmore

Les conversions de Convert toujours arrondies ainsi:

int height2 = Convert.ToInt32(width2 * height1 / (double)width1);
0
Tod

Une approche raisonnablement efficace si la division finale se compose d’un nombre pair et que le résultat sera toujours positif consiste à diviser par deux cette valeur, à en ajouter un et à le diviser par deux. Si le résultat peut être négatif, vous devez si possible ajouter un montant qui rendra tout positif, effectuer le calcul, puis soustraire un montant correspondant par la suite.

Si la division finale se fera par un nombre impair, multipliez le numérateur et le dénominateur par 2, puis procédez comme ci-dessus. Par exemple, pour calculer un * 5/7, arrondi, calculez (a*10+1)>>1. La première chose à surveiller est que vous devrez peut-être étendre vos valeurs à un type numérique plus grand pour éviter les débordements ou, si cela n’est pas possible, subdiviser la division en parties. Par exemple, pour calculer un * 14/15, vous pouvez calculer ((a * 4/3 * 7)/5 + 1)/2. Ce calcul peut toujours déborder si a est trop grand, mais la plage autorisée sera trois fois plus grande que si elle était évaluée sans diviser par 3 avant l’autre division. Notez que la subdivision de l'opération rend l'arrondi un peu moins précis, mais reste suffisamment proche pour de nombreux objectifs.

0
supercat

Le message de Jeffery est très élaboré, car vous avez généralement plus de chances de décimales nécessaires tronquées que de déborder d'un entier de 32 bits (et parce que multiplication & division est commutative), vous devez généralement effectuer la multiplication avant la division.

0
James Curran