Table des matières:
Vidéo: Coding Challenge #112: 3D Rendering with Rotation and Projection 2024
Croyez-le ou non, les ordinateurs - même les plus puissants - ont certaines limites quand il s'agit d'effectuer des calculs mathématiques. Ces limitations sont généralement insignifiantes, mais parfois ils se faufiler et vous mordre. Voici les choses dont vous devez faire attention lorsque vous faites des maths en Java.
Débordement d'entier
Le problème de base avec les types entiers est qu'ils ont une taille fixe. Par conséquent, la taille des nombres pouvant être stockés dans des variables de type
court
,
int
ou
long
est limitée. Bien que les variables
longues
puissent contenir des nombres énormes, tôt ou tard, vous tombez sur un nombre trop grand pour tenir dans une variable
longue
.
Bon, considérez cet exemple (certes inventé):
int a = 1000000000;
Système. en dehors. println (a);
a + = 1000000000;
Système. en dehors. println (a);
a + = 1000000000;
Système. en dehors. println (a);
a + = 1000000000;
Système. en dehors. println (a);
Ici, vous vous attendez à ce que la valeur de
a
augmente après chaque addition. Mais voici la sortie qui est affichée:
1000000000
2000000000
-1294967296
-294967296
La première addition semble fonctionner, mais après cela, le nombre devient négatif! En effet, la valeur a atteint la taille limite du type de données
int
. Malheureusement, Java ne vous dit pas que cette erreur est survenue. Il encapsule simplement la variable
int
avec autant de bits que possible, rejette tous les bits qui ne conviennent pas, et espère que vous ne le remarquerez pas. En raison de la façon dont
int
stocke les valeurs négatives, les grandes valeurs positives deviennent soudainement de grandes valeurs négatives.
La morale de l'histoire est que si vous travaillez avec de grands entiers, vous devriez utiliser
long
plutôt que
int
, car
long
peut stocker des nombres beaucoup plus grands que
int
. Si vos programmes traitent des nombres suffisamment grands pour être un problème pour
long
, pensez plutôt à utiliser des types à virgule flottante. Les types à virgule flottante peuvent gérer des valeurs encore plus grandes que
long
, et ils vous permettent de savoir quand vous dépassez leur capacité.
Bizarrerie à virgule flottante
Les nombres à virgule flottante ont leurs propres problèmes. Pour les débutants, les nombres à virgule flottante sont stockés en utilisant le système de nombres binaires (base 2), mais les humains travaillent avec des nombres dans le système de nombre décimal (base 10). Malheureusement, la conversion exacte des nombres entre ces deux systèmes est parfois impossible. C'est parce que dans n'importe quelle base de nombre, certaines fractions ne peuvent pas être représentées exactement.
Un exemple: Base 10 n'a aucun moyen de représenter exactement la fraction 1/3. Vous pouvez l'approximer comme 0 3333333, mais finalement vous atteignez la limite du nombre de chiffres que vous pouvez stocker, donc vous devez vous arrêter. En base 2, il arrive que l'une des fractions que vous ne pouvez pas représenter avec précision est la valeur décimale 1/10. En d'autres termes, une variable
float
ou
double
ne peut pas représenter exactement
0. 1
.
Essayez d'exécuter ce code:
float x = 0. 1f;
NumberFormat nf = NumberFormat. getNumberInstance ();
nf. setMinimumFractionDigits (10);
Système. en dehors. println (format nf (x));
La sortie résultante est la suivante:
0. 1000000015
Bien que
0. 1000000015
est fermer à
0. 1
, ce n'est pas exact.
Dans la plupart des cas, le calcul à virgule flottante de Java est suffisamment proche pour ne pas avoir d'importance. La marge d'erreur est extrêmement faible. Si vous utilisez Java pour mesurer la taille de votre maison, vous aurez besoin d'un microscope électronique pour remarquer l'erreur. Si vous écrivez des applications qui traitent des transactions financières, l'arrondissement normal peut parfois amplifier les erreurs pour les rendre significatives. Vous pouvez facturer un sou trop ou trop peu de taxe de vente. Et dans les cas extrêmes, vos factures peuvent avoir des erreurs d'ajout évidentes.
Les types entiers sont stockés en binaire, bien sûr. Mais les entiers ne sont pas soumis aux mêmes erreurs que les types à virgule flottante car les entiers ne représentent pas du tout les fractions. Vous n'avez donc pas à vous soucier de ce type d'erreur pour les types
entier
.
Division par zéro
Selon les règles de base des mathématiques, vous ne pouvez pas diviser un nombre par zéro. La raison est simple: la division est l'inverse de la multiplication - ce qui signifie que si
a * b = c
, il est également vrai que
a = c / b
. Si vous deviez laisser
b
être zéro, la division n'aurait aucun sens, car tout nombre multiplié par zéro est égal à zéro. Par conséquent,
a
et
c
doivent tous deux être égaux à zéro. En bref, les mathématiciens ont résolu ce dilemme il y a des siècles en disant que la division par zéro n'est tout simplement pas autorisée.
Que se passe-t-il si vous essayez de diviser un nombre par zéro dans un programme Java? La réponse dépend de la division des entiers ou des nombres à virgule flottante. Si vous divisez des entiers, l'instruction qui tente la division par zéro étouffe ce que l'on appelle une exception,, ce qui est une manière impolie de planter le programme.
Il existe un moyen d'intercepter cette exception pour permettre à votre programme de continuer ce que vous ne trouvez pas ici. En attendant, tout programme que vous écrivez qui tente une division entière par des plantages zéro.
Si vous essayez de diviser un type à virgule flottante par zéro, les résultats ne sont pas si brusques. À la place, Java affecte au résultat à virgule flottante l'une des valeurs spéciales répertoriées dans le tableau ci-dessous. Les paragraphes suivants expliquent comment ces valeurs spéciales sont déterminées:
- Si vous divisez un nombre par zéro et que le signe des deux nombres est le même, le résultat est l'infini positif.
0. 0
divisé par0. 0
est l'infini positif, tout comme-34. 0
divisé par-0. 0
. - Si vous divisez un nombre par zéro et que les signes des nombres sont différents, le résultat est l'infini négatif.
-40. 0
divisé par0. 0
est l'infini négatif, tout comme34. 0
divisé par0. 0
. - Si vous divisez zéro par zéro, le résultat n'est pas un nombre (NaN), quels que soient les signes.
Constante | Signification |
POSITIVE_INFINITY
|
Infinité positive |
NEGATIVE_INFINITY
|
Infinité négative |
NaN
|
Pas un nombre |
Les zéros à virgule flottante peuvent être positifs ou négatifs. Java considère que les zéros positifs et négatifs sont égaux numériquement.
Si vous tentez d'imprimer une valeur à virgule flottante contenant l'une de ces valeurs spéciales, Java convertit la valeur en une chaîne appropriée. Supposons que vous exécutiez les instructions suivantes:
double x = Math. sqrt (-50); // Pas un nombre
double y = x;
if (x == y)
Système. en dehors. println ("x est égal à y");
La sortie console résultante est
Infinity
si
i
était
-50. 0
, la console afficherait
-Infinity
, et si
i
était zéro, la console afficherait
NaN
.
Les paragraphes suivants décrivent quelques derniers bits de bizarrerie:
-
NaN
n'est pas égal à lui-même, ce qui peut avoir des conséquences étranges. Par exemple:
double x = Math. sqrt (-50); // Pas un nombre
double y = x;
if (x == y)
Système. en dehors. println ("x est égal à y");
Supposons simplement, pour les besoins de l'argument, que l'instruction
if
teste si la variable
x
est égale à la variable
y
. Parce que ce test suit immédiatement une instruction d'assignation qui assigne la valeur
x
à
y
, vous pouvez supposer que
x
est égal à
y
, droite?
Mauvais. Parce que
x
est
NaN
,
y
est également
NaN
.
NaN
n'est jamais considéré comme égal à une autre valeur, y compris une autre
NaN
. Ainsi, la comparaison dans l'instruction
if
échoue.
- Une autre conséquence étrange: Vous ne pouvez pas supposer qu'un nombre moins lui-même est toujours zéro. Considérez cette déclaration:
double z = x - x; // pas nécessairement zéro
Cette instruction ne devrait-elle pas toujours mettre
z
à zéro? Pas si
x
est
NaN
. Dans ce cas, pas un nombre moins un nombre n'est toujours pas un nombre.
- Encore une bizarrerie: Toute opération mathématique impliquant l'infini aboutit à un autre infini ou
NaN
. Infinity + 5, par exemple, est toujours égal à l'infini, donc l'appel de Buzz Lightyear "À l'infini et au-delà! "Ça ne va tout simplement pas arriver. Mais l'infini moins l'infini vous donne …NaN
.