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.
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
tient, mais ʻa --b <0
ne tient pas tient, mais ʻa --b <= 0
ne tient pas tient, mais ʻa --b> = 0
ne tient pas tient, mais ʻa --b == 0
ne tient pasIl existe de nombreux cas.
** 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.
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.
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.
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.)
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,
est inférieur à
b`. et
b` sont égaux. est supérieur à
b`.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`. ** **
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) `.