L'autre jour, quand j'ai posé la question précédente du concours de programmation avec haskell, quand j'ai écrit la logique pour gérer l'alimentation, je voulais que le résultat final soit «12340000», mais il n'a pas passé parce que c'était «12340000.0». C'est gênant.
C'est embarrassant, mais écouter est aussi un commentaire sur la honte d'une vie, et ce que vous ne savez pas peut être fait si vous apprenez et maîtrisez docilement. Tu ne peux pas?
Je ne suis pas sûr, mais en bref, c'est une histoire sur la mise en place de choses comme «int» que j'ai utilisé d'une manière vague.
Je crée généralement un système de gestion de contrat avec java / DDD, mais les nombres que je gère ne sont que des dizaines de milliers d'entiers. C'est un peu yen, en comptant le nombre de contrats, et ainsi de suite. Il n'y a pas de facturation quotidienne pour le mois. Je suis surpris.
Grâce à l'objet de valeur de DDD, je ne touche pas souvent à ʻint` brut.
Donc, juste avant d'écrire cet article, par exemple, le degré de compréhension de Java est comme ça.
" Byte
? Je ne sais pas, mais j'ai peur. "
"" Long "?
" BigInteger
? Dekaso ~ "
"Flotteur"? Fluffy ~ "
" Double
? Qu'est-ce que double ~?
Le degré de compréhension. C'est sérieux.
(Je savais seulement que le double italien était doppio)
Alors, reconnaissez simplement que beaucoup de gens l'ont mis ensemble!
J'utilise java comme langue de confirmation pour cet article.
À l'origine, je l'ai vérifié avec haskell, mais j'ai pensé qu'il serait préférable de vérifier certaines langues, alors je l'ai également essayé avec java.
Je l'ai vérifié un peu avec python, mais je pense que je vais utiliser haskell, qui est ma vraie vie, et java, que j'utilise au travail.
Alors, merci pour votre coopération. → Comprendre la différence entre Haskell's Int et Integer, Float, Double et Rational
Avant d'entrer dans la programmation, parlons des mathématiques.
Même si vous parlez de mathématiques, des choses effrayantes comme l'algèbre et la théorie des sphères ne sortent pas. J'ai peur aussi. Il s'agit du lycée.
Regardez ceci en premier.
Je vais l'expliquer grossièrement.
Les nombres que vous voyez habituellement sont généralement des «nombres réels».
D'un autre côté, les «nombres imaginaires» sont inventés parce qu'ils sont pratiques, mais ils n'existent pas réellement. Un exemple typique est «√-1» ou il est exprimé par «i». C'est facile à comprendre. Je ne suis pas sûr.
Le «nombre imaginaire» est ** un nombre qui correspond à un nombre réel inférieur à 0 **, et le «nombre réel» est défini comme ** sinon **.
Je ne veux pas y réfléchir en détail, il est donc normal d'utiliser "about` nombre réel" ".
La classification des «nombres réels» est prise au sérieux.
Les «nombres réels» sont grossièrement divisés en «nombres rationnels» et «nombres raisonnables», mais les «nombres rationnels» sont des ** nombres qui peuvent être représentés par des rapports d'entiers **.
D'autre part, d'autres nombres ** qui ne peuvent pas être exprimés par le rapport d'entiers sont appelés «nombres non raisonnables».
Les deux sont des «nombres raisonnables».
Il n'est pas nécessaire d'expliquer «entier». «3» peut être exprimé par «3/1», c'est donc un «nombre raisonnable».
Une fraction finie est une "fraction" avec une fin, telle que "0,5". Il peut être exprimé sous la forme d'un rapport «entier» comme «1/2».
D'autre part, les "fractions" telles que "0,333 ..." et "0,142857142857142857 ..." dans lesquelles le même nombre est répété indéfiniment sont appelées "fractions circulaires". Cela peut également être exprimé sous la forme d'un rapport de "nombres entiers" tels que "1/3" et "1/7".
ʻInteger` est le plus connu, donc je ne pense pas que ce soit un problème, mais pour le moment.
L '«entier négatif» est «-1» ou «-5», qui peut être exprimé sous la forme de «-5 / 1».
«0» est «0/1», n'est-ce pas?
La même chose est vraie pour les «entiers positifs».
De plus, un «entier positif» est appelé un «nombre naturel». (Peu importe si vous incluez «0» dans cet article)
Un supplément à «minorité» et «fraction».
Une «fraction» est un nombre exprimé par ** un rapport de nombres **, et à première vue, il semble être identique à un «nombre rationnel». Cependant, comme les «nombres rationnels» sont des ** ratios d'entiers **, les fractions sont un concept plus large.
Par exemple, il y a «1 / √2». Il s'agit d'un "nombre raisonnable" car ce n'est pas un ** rapport d'entiers **. (Comme il est d'environ 0,7, lorsqu'il est au carré, il est d'environ «0,5», ce qui est plus grand que «0», donc ce n'est pas un «nombre imaginaire».)
De plus, par exemple, il existe un "nombre infini", mais dans la figure précédente, le "nombre circulaire" et le "nombre irrationnel" du "nombre rationnel" sont tous deux des "nombres infinis". C'est la différence entre circulant et non circulant.
Tout en gardant à l'esprit les points ci-dessus, nous, les programmeurs, devons connaître les mots anglais, nous allons donc les résumer grossièrement. (Bien que l'anglais soit également inclus dans l'image.)
Le «nombre réel» est le «nombre réel» et le «nombre imaginaire» est le «nombre imaginaire», il est donc facile d'obtenir une image.
Pas très familier, mais le «nombre rationnel» est le «nombre rationnel». Si vous l'écrivez en copie, vous le frapperez rarement.
Puisque «ratio» signifie «ratio», certaines personnes peuvent l'avoir utilisé dans le nom de la variable.
De plus, bien que non montré dans l'image, «décimal» est «décimal» et «fraction» est «fraction».
J'aimerais voir l'exemple de code en java à la fois, mais il y a de grandes différences entre le monde humain et le monde informatique.
Autrement dit, «la mémoire est finie».
Là où il se rapporte, par exemple, "grand nombre" et "fraction infinie".
Par exemple, ʻint de java est un ʻentier
fixe de 32 bits.
En raison de la mémoire limitée d'un ordinateur, nous limitons nos nombres à la plage de 32 «0 | 1».
D'autre part, "multi-fold integer" est une méthode d'expression de nombres ** qui alloue dynamiquement de la mémoire en fonction du nombre à traiter.
En théorie, vous pouvez gérer un nombre infini. (Bien sûr, tant que la mémoire de l'ordinateur le permet.)
Cet entier de longueur fixe
provoque le débordement que tout le monde aime.
Par exemple, java'byte` est un "entier" fixe de 8 bits. Le premier bit est utilisé comme signe positif / négatif et le reste est utilisé pour représenter la valeur.
0000|0000
De1
Commençant à augmenter petit à petit0111|1111
De1000|0000
Débordement où
1111|1111
De1|0000|0000
Le 9ème bit est hors de portée au point où0000|0000
Sera traité comme.
(|
Est inséré tous les 4 chiffres pour une visualisation facile.)
D'autre part, «BigInteger» est «un entier de plusieurs longueurs». Lorsqu'un débordement de chiffres est sur le point de se produire, il alloue dynamiquement de la mémoire afin qu'il ne déborde pas.
(La figure ci-dessus est une image car elle dépend de la méthode de montage pour contenir le code et la valeur.)
L '«entier de longueur fixe» est excellent en termes d'efficacité et de performance de la mémoire, et l' «entier de longueur multiple» est d'une excellente précision. Ce sont les bonnes personnes au bon endroit.
Il existe une idée similaire pour les «fractions» ainsi que pour les «entiers».
«Virgule flottante» est l'une des ** méthodes de représentation des nombres **, et est une méthode de représentation qui a une «partie formelle» et une «partie exposante» de «longueur fixe».
En gros, on peut penser que la «partie implicite» représente une valeur et la «partie exponentielle» représente un chiffre.
Par exemple, le nombre binaire «0,00000101» est exprimé par «101 * 2 ^ -8».
Cependant, dans ce cas, il peut être exprimé par «10.1 * 2 ^ -7», il est donc décidé que la «partie incorrecte» devrait être «1.x» dans la norme appelée «IEEE754». Donc c'est "1.01 * 2 ^ -6". J'écris aussi «1.01e-6».
C'est celui avec «e» qui apparaît parfois lors de l'écriture de code. J'avais peur mais je l'ai surmonté.
Je me demande si cela s'appelle «virgule flottante» parce que la position pour atteindre la virgule décimale change en fonction de la «partie formelle» et de la «partie exponentielle». D'autre part, le mot apparié est «décimal fixe», qui comprend, par exemple, «entier».
L'introduction est devenue plus longue. De là, nous vérifierons avec Gashigashi java.
type | description |
---|---|
octet, octet | 8 bits entier de longueur fixe |
court, court | entier de longueur fixe 16 bits |
int, Integer | Entier de longueur fixe 32 bits |
long, Long | Entier de longueur fixe de 64 bits |
float, Float | Virgule flottante simple précision (32 bits) |
double, Double | Virgule flottante double précision (64 bits) |
BigInteger | Entier multiple |
BigDecimal |
Le code ci-dessous omet l'équivalent System.out.println
et le commentaire sur cette ligne est le résultat.
byte, short, int, long Il y en a beaucoup, mais n'ayez pas peur.
Ce sont tous des «entiers de longueur fixe» et la seule différence est la précision qu'ils peuvent exprimer.
Byte.MAX_VALUE; // 127
Short.MAX_VALUE; // 32767
Integer.MAX_VALUE; // 2147483647
Long.MAX_VALUE; // 9223372036854775807
Par exemple, si vous entrez + 1
à la limite supérieure de ʻInteger`, il débordera.
Integer.MAX_VALUE + 1; // -2147483648
Et bien sûr, passer de moins précis à plus précis est bien, mais pas l'inverse.
short s = 20000;
(int) s; // 20000
int i = 40000;
(short) i; // -25536
C'est différent de l'objectif initial, mais c'est étonnamment intéressant, alors j'aimerais le dire.
En java, ʻint est un type primitif et ʻInteger
est un type de classe.
Les principales différences sont, en gros, "ʻint n'autorise pas
null" et "ʻint
ne peut pas être T
tel queList <T>
".
Il n'y a aucune différence entre ʻint et ʻInteger
en termes d'exactitude. C'est important.
De plus, java a un mécanisme qui permet au compilateur de se convertir correctement, donc dans la plupart des cas, vous n'avez pas à vous en soucier trop.
Vous n'y pensez peut-être pas trop, mais je vais vous expliquer la zone de pile et la zone de tas très grossièrement.
Par exemple, si vous écrivez un code comme celui-ci.
(Pour faciliter la distinction entre les variables ʻint et ʻInteger
, ** cet article utilise des lettres majuscules au début du nom de la variable.)
Integer Ia = new Integer(1);
Dans ce cas, la mémoire ressemble à ceci.
Si vous faites new
, quelque chose sera mis dans la variable ʻIa` dans la zone de pile.
D'une manière ou d'une autre, j'ai l'impression que «Ia» contient l'instance elle-même, mais que seule la ** flèche ** la contient. D'une manière effrayante, c'est un ** pointeur **.
L'instance créée se trouve dans la zone du tas.
D'un autre côté, le type primitif ʻint` est réservé tel quel dans la zone de pile.
Integer Ia = new Integer(1);
Integer Ib = new Integer(1);
int ia = 1;
int ib = 1;
Donc, si vous écrivez ce code, l'image ressemblera à celle ci-dessous.
Je suis sûr qu'il y a beaucoup de gens qui ont été en colère contre les gens effrayants qui disent: "N'utilisez pas ==
pour une comparaison en java ", mais voyons pourquoi.
L'identité dans un type de classe consiste à comparer ** la même instance ** et l '«équivalence» est ** la même valeur **.
Le premier est fait par ==
et le second par ʻequals`. L'équivalence dépend également de la mise en œuvre.
(Par exemple, dans la comparaison d'entités DDD, seule la correspondance d'identité peut être considérée comme la même valeur.)
Le type primitif ==
compare simplement les valeurs.
Donc ʻIa == Ibest ** faux ** car c'est une flèche avec une destination différente. ʻIa.equals (Ib)
est ** true ** car les valeurs de destination sont les mêmes.
Par exemple, "M. A et M. B ont tous deux des boules de 500 yens, ** des pièces physiquement différentes **, mais ** la même valeur **".
Maintenant que vous comprenez la zone de pile, la zone de tas et la comparaison, il s'agit de la conversion mutuelle.
ʻInt-> ʻInteger
est appelé ** boxing ** et vice versa ** unboxing **.
Je pense que c'est une image à mettre dans une boîte de classe wrapper.
Le code suivant peut être exécuté par ** auto boxing | auto unboxing **.
Integer Ia = new Integer(1);
int ia = Ia; // unboxing
int ib = 1;
Integer Ib = ib; // boxing
En interne, les valeurs sont apportées à la zone de pile et des instances sont créées dans la zone de tas pour obtenir des références. (En fait, la valeur d'origine ne disparaît pas, mais elle est mince car c'est facile à imaginer.)
Maintenant, lequel des codes suivants serait «vrai» ou «faux»?
int ia = 1;
int ib = 1;
Integer Ia = ia;
Integer Ib = ib;
Ia == Ib; // true or false ?
Les flèches pour «Ia» et «Ib» devraient être différentes car elles sont «nouvelles» par ** auto boxing **. C'est également le cas sur l'image ci-dessus.
Mais c'est "vrai".
Apparemment, le ** auto boxing ** est réalisé par ʻInteger # valueOf, et le ** auto unboxing ** est réalisé par ʻInteger # intValue
.
Integer Ia = Integer.valueOf(ia);
Integer Ib = Integer.valueOf(ib);
Donc, l'essentiel ʻInteger # valueOf`, mais il est implémenté comme ça.
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
Apparemment, le "-128" ~ "127" fréquemment utilisé semble être mis en cache. Donc, dans l'exemple de code ci-dessus, ce n'est pas «nouveau».
Avec un tel code, ce sera «faux» correctement, il semble que la compréhension était correcte et que c'est sûr.
int ia = 1000;
int ib = 1000;
Integer Ia = ia;
Integer Ib = ib;
Ia == Ib; // false
Au fait, en interne, utiliser ʻInteger # intValue pour ** unboxing automatique ** signifie que si ʻIa
est null
, ** unboxing automatique ** entraînera une NullPointerException
.
C'est vrai.
Dans cet article, ʻint et ʻInteger
et float
et Float
n'ont aucune différence de précision, nous utiliserons donc celui qui vous convient dans l'exemple de code sans préavis.
float, double Vous avez maintenu le «entier». Vient ensuite le «fractionnaire».
Je me demandais ce qu'est le «double», mais c'est clair si vous étudiez.
Le float
utilisait 32 bits et le double
utilisait 64 bits pour représenter la valeur. Donc double précision.
Puisqu'il est impossible de représenter complètement une «fraction infinie» tant que la mémoire de l'ordinateur est finie, elle doit être traitée en supposant qu'une erreur se produira.
Par exemple, le nombre décimal «0,01» ne peut pas être exprimé sous forme de nombre fini s'il s'agit d'un nombre binaire. Comme il ne peut pas être exprimé sous forme de nombre fini, vous devez abandonner quelque part, et si vous le répétez, vous pouvez comprendre que l'erreur augmentera.
Alors, quel genre d'erreur se produira? Essayons.
float f = 0;
for (int i = 0; i < 100; i++) {
f += 0.01f;
}
double d = 0;
for (int i = 0; i < 100; i++) {
d += 0.01d;
}
f; // 0.99999934
d; // 1.0000000000000007
«double» est plus proche de «1.0».
La conversion entre «float» et «double», comme «short» et «int », s'arrête lorsqu'elle est convertie de la précision supérieure à la précision inférieure.
f; // 0.99999934
d; // 1.0000000000000007
(double) f; // 0.9999993443489075
(float) d; // 1.0
Si vous passez de «double» à «float», il manque.
De plus, comme il est fini en premier lieu, une erreur se produira simplement avec les valeurs suivantes.
10d / 3d; // 3.3333333333333335
1.00000001f; // 1.0
BigInteger, BigDecimal Merci d'avoir attendu, les gars «multi-longueurs».
Ils allouent la mémoire de manière dynamique en fonction des chiffres, donc il n'y a pas de débordement et pas d'erreur. En quelque sorte incroyable.
Essayons-le tout de suite.
BigDecimal Essayez à partir de la «grande décimale» de la «fraction». Traitons généreusement un énorme «entier» dès le début.
BigDecimal bd = new BigDecimal(Long.MAX_VALUE);
bd; // 9223372036854775807
bd.add(new BigDecimal(1)); // 9223372036854775808
Même s'il est ajouté à la limite supérieure de «long», il ne déborde pas. Il est normal d'ajouter plus audacieusement.
bd.add(bd); // 18446744073709551614
Vous pouvez également ajouter des «fractions».
bd.add(new BigDecimal(0.5)); // 9223372036854775807.5
préféré? Qu'en est-il de l'erreur «fractionnaire» de?
BigDecimal bd = BigDecimal.ZERO;
BigDecimal x = new BigDecimal(0.01);
for (int i = 0; i < 100; i++) {
bd = bd.add(x);
}
bd; // 1.00000000000000002081668171172168513294309377670288085937500
C'est plus précis que «1.0000000000000007» de «double». (ToString
est fait parce que je fais de mon mieux.)
Qu'en est-il de 10d / 3d
, qui a une erreur dans double
?
BigDecimal bd10 = new BigDecimal(10);
BigDecimal bd3 = new BigDecimal(3);
bd10.divide(bd3); // ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
J'ai vu le mot «se terminer» dans le truc qui ressemble à une figurine de Ben, et je suis en colère que ce ne soit pas une fraction finie.
Il semble que la valeur avec l'erreur ne sera pas conservée avec l'erreur. Cela semble inutile si vous ne spécifiez pas s'il faut couper ou arrondir.
bd10.divide(bd3, RoundingMode.FLOOR) // 3
bd10.divide(bd3, RoundingMode.CEILING) // 4
BigInteger Ce mec est facile. C'est un «grand décimal» qui ne peut pas gérer les «fractions».
BigInteger bi = BigInteger.valueOf(Long.MAX_VALUE);
bi; // 9223372036854775807
bi.add(bi); // 18446744073709551614
BigInteger
n'a pas de méthode de génération qui vous permet de passer une fraction
comme 0.5
, donc c'est juste cela par rapport à BigDecimal
.
Tout va bien maintenant. Pas effrayant.
Au fait, si cela ressemble à java, n'avez-vous pas envie de le détruire avec ʻadd`? C'est comme «List # add».
Cependant, si vous comprenez que vous pouvez réallouer de la mémoire à chaque fois que vous ajoutez`, il est facile de penser que vous créez une instance non destructive et différente à chaque fois. (Cela dépend de la méthode d'implémentation, donc il peut être immuable, mais il peut être mutable.)
Cela a été un long article, mais il n'y a que trois points principaux d'expression numérique en java que j'ai ressenti après l'avoir essayé!
byte
, short
, ʻintet
long` est la précision, et chacun a ses propres limites.BigInteger
et BigDecimal
sont illimités ʻentier et
fraction` (tant qu'il y a de la mémoire)C'est tout! La différence entre ʻint et ʻInteger
est que je ferai de mon mieux pour étudier java plutôt que l'expression numérique!
Bref, j'ai beaucoup appris. J'étais parfaitement conscient de la façon dont je venais habituellement.
Et que faire si vous comprenez cela, c'est que vous voulez le séparer de la logique du domaine, alors créez un objet de valeur et cachez-le! Je le comprends exactement, donc je ne l'utilise pas dans mon travail quotidien (implémentation de domaine)! Quel paradoxe!
Recommended Posts