[JAVA] La fin de la programmation catastrophique # 03 "Comparaison d'entiers, si" a> b ", supposons que c'est" a --b> 0 ""

Un programmeur ordinaire le comprend intuitivement et par réflexe, Je voudrais écrire un article sur une histoire rudimentaire et simple. En raison de la nature rudimentaire, un programmeur lourd comme moi Je le fais bien (même si je pensais l'avoir compris).

S'il y a des erreurs ou du contenu inapproprié, veuillez commenter Nous examinerons les corrections et ajustements du contenu.

Cette fois, "La fin de la programmation catastrophique # 01" Faites-le avec la valeur absolue d'un entier "" J'ai écrit un article basé sur le post dans la section commentaires de. Merci pour votre commentaire.

1. Comparaison du côté gauche et du côté droit

La programmation est parfois mathématique, parfois pas mathématique.

Dans certains domaines des mathématiques Si ʻa> b, alors ʻa --b> 0 serait correct.

En programmation Il existe de nombreux cas où ʻa> b est valable, mais ʻa --b> 0 ne l'est pas.

De même

Il existe de nombreux cas.

2. Cette fois, nous parlons de type entier

** Cette fois, je vais le limiter au type entier **. ** S'il s'agit d'un type à virgule flottante, vous devez faire attention dans une autre dimension **, donc J'écrirai un article à un autre moment.

Pour rendre l'histoire plus facile Considérez un type entier signé 32 bits. En Java, C # et C / C ++, il s'appelle le type int. (En C / C ++, un système de traitement avec un type int de 32 bits est supposé. Pour certaines personnes, vous pouvez imaginer int32 ou Int32. )

La plage qu'un type entier signé 32 bits peut représenter est, pour de nombreux langages de programmation, «-2147483648 à 2147483647» Ça devrait être.

1.3. Essayons-le pour le moment

Essayez d'exécuter le code suivant autour de Java et C #. Il montre juste les valeurs booléennes (True ou False) de ʻa> b et ʻa --b> 0. Essayez de mettre des valeurs entre «a» et «b».

Pour le moment, je vais l'essayer avec C #. Même si vous le faites en Java ou C / C ++, la partie sortie de l'écran est légèrement différente.

//Entrez des valeurs pour chacun de a et b et essayez différentes choses.
int a = 100;
int b = 100;

//Pour Java, System.out.printf("a > b : %s%n", (a > b));Et.
Console.WriteLine("a > b : {0}",     (a > b));
Console.WriteLine("a - b > 0 : {0}", (a - b > 0));

Après en avoir essayé, ça ressemble à ça.

Valeur d'un Valeur de b a > b a - b > 0
100 100 False False
100 50 True True
-2000000000 500000000 False True
2000000000 -500000000 True False

Dans les deux premiers, la vérité de ʻa> b et ʻa --b> 0 correspond, Les deux derniers ne correspondent pas.

3. Soyez toujours prudent même dans les quatre règles

Vous devez toujours faire attention même avec des opérations simples telles que l'addition, la soustraction, la multiplication et la division (+, -, \ *, /).

Pour le type entier L'addition, la soustraction, la multiplication et la division (+, -, \ *, /) peuvent ** déborder **. De plus, la division entière (/) entraînera une erreur (exception, plantage, etc.) lorsqu'elle sera divisée par ** 0 dans de nombreux langages et environnements **.

De plus, les divisions qui incluent des ** entiers négatifs peuvent créer des situations difficiles **.

Par exemple

int a = 5;
int b = -2;
int answer = a / b;

Si vous le faites, il y a des langues / environnements où «réponse» devient «-2» et des langues / environnements où il devient «-3». Par exemple, si l'opération de ** troncature ** est appliquée à la division des nombres entiers, c'est «-2», Si cela fonctionne comme ** fonction étage **, Puisqu'une valeur entière inférieure au résultat du calcul est adoptée, elle devient «-3».

De même, les opérations résiduelles (%) contenant des entiers négatifs peuvent se comporter différemment selon le langage et l'environnement.

Si vous pensez que la division entière (/) ne débordera pas, Vous pouvez essayer (valeur minimale de type int) / (-1). (Cette opération peut être traitée spécialement selon la langue et l'environnement.)

C'est un peu hors sujet, mais cette fois, nous envisagerons un débordement.

4. Parfois, j'oublie par inadvertance de déborder par soustraction

Si vous ne pouvez exprimer que de -2147483648 à 2147483647 Vous pouvez facilement imaginer que l'addition (+) et la multiplication (*) peuvent provoquer un débordement. Bien sûr, il y a une possibilité de débordement par soustraction (-), mais Selon la situation, vous pouvez l'oublier par inadvertance.

Voici un exemple de dépassement au-delà de la plage qui peut être exprimée.

Calcul Résultat du calcul calculé à la main Résultat du calcul en Java
2147483647 + 5 2147483652 -2147483644
2147483 * 10000 21474830000 -6480
2147483647 - (-5) 2147483652 -2147483644
-2147483648 - 5 -2147483653 2147483643

Le fonctionnement bas est en dessous de la limite inférieure qui peut être exprimée dans la plage négative, C'est aussi un débordement (débordement dans le sens négatif). Certaines personnes appellent cela "underflow", mais en général Lorsque la valeur est si proche de 0 qu'elle ne peut pas être exprimée avec une précision en virgule flottante, Il devrait être correct de l'appeler underflow. Strictement parlant, le dépassement inférieur est une mauvaise utilisation de la situation ci-dessus pour les valeurs entières. (Habituellement, c'est une plage qui peut être comprise dans le flux de l'histoire, donc il est délicat de le souligner.)

5. Fin

Lors de la création d'un programme qui effectue des calculs complexes Je pense que ce débordement me met souvent mal à l'aise.

Dans un cas simple, À première vue, il y a une possibilité de le faire dans un cas qui ne semble pas être directement lié à cette histoire.

Pour donner un exemple concret Tels que «Comparateur» et «Interface comparable», Il semble qu'il y ait des cas où le mécanisme de classement des instances d'objets est utilisé.

Par exemple, si vous souhaitez trier les instances de la liste comme le souhaite le programmeur. Si vous avez des instances A et B d'un objet C'est celui qui sert à déterminer lequel est le premier de la commande.

Dans l'exemple utilisant la classe, le code devient long, nous allons donc utiliser l'exemple de type valeur. Ce qui suit est un exemple de C #, mais il semble similaire en Java. Récemment, le nombre de langues pouvant utiliser des expressions lambda a augmenté, je vais donc écrire dans des expressions lambda. (Je peux l'écrire un peu plus clairement, mais je l'écris de manière redondante en mettant l'accent sur la clarté.)

//Tableau de valeurs entières
var array = new int[] { 3, 1, -2147483647, -5 };

//Stocker dans la liste
var list = new List<int>(array);

//Évaluer et trier par expression lambda
list.Sort(
  (a, b) =>
  {
    return (a - b);
  }
);

foreach (var item in list)
{
  Console.WriteLine(item);
}

Le point est la partie lambda.

(a, b) =>
{
  return (a - b);
}

mais,

int compare(int a, int b)
{
  return (a - b);
}

Je pense que c'est facile à comprendre.

Dans de nombreux cadres, la méthode de comparaison de Comparator, la fonction de comparaison,

Il est censé être mis en œuvre pour revenir.

Avec cette spécification, les programmeurs peuvent très bien comprendre ce qu'ils veulent faire en une seule opération. En raison des spécifications de l'application, (a --b) est S'il est clair qu'il se trouve dans la plage où il n'y a pas de possibilité de débordement Il n'y a aucun problème avec return (a --b);.

S'il existe une possibilité de débordement, le résultat du tri peut ne pas être celui que vous attendiez.

L'exemple de code ci-dessus est destiné à trier les nombres par ordre croissant, Le résultat est le suivant.

1
3
-2147483647
-5

Ce n'est pas du tout dans l'ordre croissant. ** Notez également que les résultats varieront en fonction de l'ordre initial dans le tableau d'origine ʻarray`. ** **

5. Contre-mesures

Fondamentalement, vous devez toujours faire attention à la possibilité de débordement si vous exécutez quatre règles.

Dans l'exemple Comparator, si vous n'avez pas besoin de suivre quatre règles Il existe également une méthode de mise en œuvre sans quatre règles. En programmation, une implémentation naïve et stupide peut en fait être plus sûre ou plus efficace. Il y a ok.

Dans cet exemple, ʻarray` était un tableau de type int, donc Vous n'avez pas à définir vous-même le tri en premier lieu, J'aimerais que vous le lisiez de manière appropriée lors du tri des objets que vous avez vous-même définis.

list.Sort(
  (a, b) =>
  {
    int result = -1;

    if( a > b )
    {
        result = 1;
    }
    else if( a == b )
    {
        result = 0;
    }

    return result;  
  }
);

Ou si vous utilisez un opérateur ternaire

list.Sort(
  (a, b) =>
  {
    return (a > b) ? 1 : ((a == b) ? 0 : -1);
  }
);

Ou une mise en œuvre simple fonctionnera.

Si l'objet à comparer a une implémentation comparable, Il vaudrait mieux en profiter.

Avec C #

list.Sort(
  (a, b) =>
  {
    return a.CompareTo(b);
  }
);

C'est comme ressentir.

Pour Java Je pense que vous utiliserez ʻInteger.compare (a, b) `.

Recommended Posts

La fin de la programmation catastrophique # 03 "Comparaison d'entiers, si" a> b ", supposons que c'est" a --b> 0 ""
La fin de la programmation catastrophique # 01 "Faites-le avec la valeur absolue d'un entier"
[Java] Cela peut être heureux si la valeur de retour de la méthode qui retourne null est facultative <>
Mémo: [Java] Si un fichier se trouve dans le répertoire surveillé, traitez-le.
Obtenez le type d'un élément d'un tableau pour déterminer s'il s'agit d'un tableau
Le patchForObject ajouté à RestTemplate ne peut pas être utilisé efficacement s'il s'agit d'une implémentation par défaut (celle qui utilise HttpURLConnection).
Existe-t-il une différence de performances entre Oracle JDK et OpenJDK à la fin de 2017?
Créez un package qui est une partie de développement commune d'Automation Anywhere A2019 # 1 ~ Commencez par créer et utiliser l'exemple de SDK tel quel
[Quarkus] Un piège qui ne fonctionne pas même si vous copiez et collez l'exemple GCP Pub / Sub tel quel
Au moment de l'événement dialogReturn, j'ai vérifié car il n'est pas mis à jour même si je spécifie un composant avec mise à jour
La comparaison d'énumération est ==, et equals est bonne [Java]
La question de savoir quel est le meilleur, si ou changer
Un mémo du programme que vous pouvez réaliser que la probabilité de lancer des dés est d'environ 1/6
Même si j'écris le paramètre de STRICT_QUOTE_ESCAPING dans CATALINA_OPTS dans tomcat8.5, il n'est pas reflété.
[Docker] Est-il suffisant de l'appeler une construction en plusieurs étapes? → L'histoire qui est devenue si bonne