Als ich neulich die letzte Frage des Programmierwettbewerbs mit haskell stellte, als ich die Logik zum Verwalten der Leistung schrieb, wollte ich, dass das Endergebnis "12340000" ist, aber es hat nicht bestanden, weil es "12340000.0" war. Es ist peinlich.
Es ist peinlich, aber das Zuhören ist auch ein Kommentar zur Schande Ihres Lebens, und was Sie nicht wissen, kann getan werden, wenn Sie es gehorsam lernen und beherrschen. Kannst du nicht?
Ich bin mir nicht sicher, aber kurz gesagt, es ist eine Geschichte über das Zusammenstellen von Dingen wie "int", die ich auf mehrdeutige Weise verwendet habe.
Normalerweise erstelle ich ein Vertragsverwaltungssystem mit Java / DDD, aber die Zahlen, die ich verarbeite, sind nur Zehntausende von ganzen Zahlen. Es ist ein kleiner Yen, der die Anzahl der Verträge zählt und so weiter. Es gibt keine tägliche Abrechnung für den Monat. Ich bin überrascht.
Dank des Wertobjekts von DDD berühre ich das rohe "int" nicht oft.
Kurz vor dem Schreiben dieses Artikels ist beispielsweise der Grad des Verständnisses von Java so.
" Byte
? Ich weiß es nicht, aber ich habe Angst. "
" Lang
?
" BigInteger
? Dekaso ~ "
" Float
? Fluffy ~ "
" Double
? Was ist double ~?
Der Grad des Verständnisses. Das ist ernst.
(Ich wusste nur, dass Doppelitaliener Doppio ist)
Also erkenne einfach an, dass viele Leute es zusammengestellt haben!
Ich verwende Java als Bestätigungssprache für diesen Artikel.
Ursprünglich habe ich es mit haskell überprüft, aber ich dachte, es wäre besser, einige Sprachen zu überprüfen, also habe ich es auch mit Java versucht.
Ich habe es ein wenig mit Python überprüft, aber ich denke, ich werde Haskell verwenden, das mein wirkliches Leben ist, und Java, das ich bei der Arbeit verwende.
Vielen Dank für Ihre Mitarbeit. → Den Unterschied zwischen Haskells Int und Integer, Float, Double und Rational verstehen
Bevor wir mit dem Programmieren beginnen, sprechen wir über Mathematik.
Selbst wenn Sie Mathematik sagen, kommen beängstigende Dinge wie Algebra und Sphärentheorie nicht heraus. Ich habe auch Angst. Es geht um die Junior High School.
Schau dir das zuerst an.
Ich werde es grob erklären.
Die Zahlen, die Sie normalerweise sehen, sind normalerweise "reelle Zahlen".
Andererseits werden "imaginäre Zahlen" erfunden, weil sie bequem sind, aber tatsächlich nicht existieren. Ein typisches Beispiel ist "√-1" oder es wird als "i" ausgedrückt. Es ist leicht zu verstehen. Ich bin mir nicht sicher.
Die "imaginäre Zahl" ist ** eine Zahl, die zu einer reellen Zahl kleiner als 0 ** quadriert, und die "reelle Zahl" ist definiert als ** ansonsten **.
Ich möchte nicht im Detail darüber nachdenken, daher ist es in Ordnung, "about real number
"zu verwenden.
Die Klassifizierung von "reellen Zahlen" wird ernst genommen.
"Reelle Zahlen" werden grob in "rationale Zahlen" und "irrationale Zahlen" unterteilt, aber "rationale Zahlen" sind ** Zahlen, die durch Verhältnisse von ganzen Zahlen ** dargestellt werden können.
Andererseits werden andere Zahlen **, die nicht durch das Verhältnis von ganzen Zahlen ausgedrückt werden können, als "unvernünftige Zahlen" bezeichnet.
Beides sind "vernünftige Zahlen".
Es besteht keine Notwendigkeit, "Ganzzahl" zu erklären. "3" kann als "3/1" ausgedrückt werden, es handelt sich also um eine "vernünftige Zahl".
Eine endliche Fraktion ist eine "Fraktion" mit einem Ende, wie beispielsweise "0,5". Es kann als Verhältnis von "Ganzzahl" wie "1/2" ausgedrückt werden.
Andererseits werden "Brüche" wie "0,333 ..." und "0,142857142857142857 ...", bei denen dieselbe Zahl auf unbestimmte Zeit wiederholt wird, als "kreisförmige Brüche" bezeichnet. Dies kann auch als Verhältnis von "ganzen Zahlen" wie "1/3" und "1/7" ausgedrückt werden.
Integer
ist das bekannteste, daher denke ich nicht, dass es ein Problem ist, aber vorerst.
Die "negative ganze Zahl" ist "-1" oder "-5", was in Form von "-5 / 1" ausgedrückt werden kann.
"0" ist "0/1", nicht wahr?
Gleiches gilt für "positive ganze Zahlen".
Eine "positive ganze Zahl" wird auch als "natürliche Zahl" bezeichnet. (Es spielt keine Rolle, ob Sie "0" in diesen Artikel aufnehmen.)
Eine Ergänzung zu "Minderheit" und "Bruchteil".
Ein "Bruch" ist eine Zahl, ausgedrückt durch ** ein Verhältnis von Zahlen **, und auf den ersten Blick scheint es dasselbe zu sein wie eine "rationale Zahl". Da "rationale Zahlen" jedoch ** Verhältnisse von ganzen Zahlen ** sind, sind Brüche ein umfassenderes Konzept.
Zum Beispiel gibt es "1 / √2". Dies ist eine "vernünftige Zahl", da es sich nicht um ein ** Verhältnis von ganzen Zahlen ** handelt. (Da es im Quadrat ungefähr 0,7 ist, ist es ungefähr "0,5", was größer als "0" ist, also ist es keine "imaginäre Zahl".)
Zum Beispiel gibt es auch eine "unendliche Zahl", aber in der vorherigen Figur sind sowohl die "kreisförmige Zahl" als auch die "irrationale Zahl" der "rationalen Zahl" "unendliche Zahlen". Es ist der Unterschied zwischen zirkulierend und nicht zirkulierend.
Unter Berücksichtigung der oben genannten Punkte müssen wir Programmierer englische Wörter kennen, damit wir sie grob zusammenfassen können. (Obwohl Englisch auch im Bild enthalten ist.)
"Reelle Zahl" ist "reelle Zahl" und "imaginäre Zahl" ist "imaginäre Zahl", so dass es einfach ist, ein Bild zu erhalten.
Nicht sehr vertraut, aber die "rationale Zahl" ist die "rationale Zahl". Wenn Sie es in Kopie schreiben, werden Sie es selten treffen.
Da "Verhältnis" "Verhältnis" bedeutet, haben einige Leute es möglicherweise im Variablennamen verwendet.
Auch wenn im Bild nicht gezeigt, ist "Dezimal" "Dezimal" und "Bruch" ist "Bruch".
Ich würde den Beispielcode gerne sofort in Java sehen, aber es gibt große Unterschiede zwischen der menschlichen Welt und der Computerwelt.
Das heißt, "Erinnerung ist endlich".
Wo es sich bezieht, ist zum Beispiel "große Zahl" und "unendlicher Bruch".
Zum Beispiel ist Java int
eine feste 32-Bit-Ganzzahl`.
Aufgrund des begrenzten Speichers eines Computers beschränken wir unsere Zahlen auf den Bereich von 32 "0 | 1".
Andererseits ist "mehrfache Ganzzahl" eine Methode zum Ausdrücken von Zahlen **, die den Speicher dynamisch entsprechend der zu behandelnden Zahl zuweist.
Theoretisch können Sie mit einer unendlichen Zahl umgehen. (Natürlich, solange der Computerspeicher dies zulässt.)
Diese "Ganzzahl fester Länge" verursacht den Überlauf, den jeder liebt.
Zum Beispiel ist java'byte eine feste 8-Bit-Ganzzahl
.
Das erste Bit wird als positives / negatives Vorzeichen verwendet, und der Rest wird zur Darstellung des Werts verwendet.
0000|0000
Von1
Nach und nach zuzunehmen0111|1111
Von1000|0000
Überlauf wo
1111|1111
Von1|0000|0000
Das 9. Bit befindet sich an der Stelle außerhalb des Bereichs, an der0000|0000
Wird behandelt als.
(|
Wird zur leichteren Anzeige alle 4 Ziffern eingefügt.)
Andererseits ist "BigInteger" eine multiplizierte ganze Zahl. Wenn ein Ziffernüberlauf auftritt, wird der Speicher dynamisch zugewiesen, damit er nicht überläuft.
(Die obige Abbildung ist ein Bild, da sie von der Montagemethode zum Speichern des Codes und des Werts abhängt.)
Die "Ganzzahl mit fester Länge" weist eine hervorragende Speichereffizienz und Leistung auf, und die "Ganzzahl mit mehreren Längen" weist eine hervorragende Genauigkeit auf. Das sind die richtigen Leute am richtigen Ort.
Es gibt eine ähnliche Idee sowohl für "Brüche" als auch für "ganze Zahlen".
"Gleitkomma" ist eine der ** Darstellungsmethoden für numerische Werte ** und eine Darstellungsmethode, die einen "formalen Teil" und einen "Exponententeil" mit "fester Länge" aufweist.
Grob gesagt ist es in Ordnung zu glauben, dass der "implizite Teil" einen Wert und der "exponentielle Teil" eine Ziffer darstellt.
Zum Beispiel wird die Binärzahl "0.00000101" als "101 * 2 ^ -8" ausgedrückt.
Damit kann es jedoch als "10.1 * 2 ^ -7" ausgedrückt werden, so dass entschieden wird, dass der "unzulässige Teil" in der Norm "IEEE754" "1.x" sein sollte. Es ist also 1.01 * 2 ^ -6
.
Ich schreibe auch 1.01e-6
.
Dies ist die mit "e", die manchmal beim Schreiben von Code erscheint. Ich hatte Angst, aber ich habe es überwunden.
Ich frage mich, ob es "Gleitkomma" heißt, weil sich die Position zum Erreichen des Dezimalpunkts abhängig vom "formalen Teil" und dem "exponentiellen Teil" ändert. Andererseits ist das gepaarte Wort ein "fester Dezimalpunkt", der beispielsweise eine "ganze Zahl" enthält.
Die Einführung ist länger geworden. Von hier aus werden wir uns bei Gashigashi Java erkundigen.
Typ | Beschreibung |
---|---|
Byte, Byte | 8bit Ganzzahl mit fester Länge |
kurze, kurze | 16-Bit-Ganzzahl mit fester Länge |
int, Integer | 32bit Integer mit fester Länge |
long, Long | 64bit Integer mit fester Länge |
float, Float | Gleitkomma mit einfacher Genauigkeit (32 Bit) |
double, Double | Gleitkomma mit doppelter Genauigkeit (64 Bit) |
BigInteger | Multiple Integer |
BigDecimal |
Der folgende Code lässt das Äquivalent "System.out.println" weg und der Kommentar in dieser Zeile ist das Ergebnis.
byte, short, int, long Es gibt viele, aber keine Angst.
Sie sind alle "Ganzzahlen fester Länge" und der einzige Unterschied ist die Genauigkeit, die sie ausdrücken können.
Byte.MAX_VALUE; // 127
Short.MAX_VALUE; // 32767
Integer.MAX_VALUE; // 2147483647
Long.MAX_VALUE; // 9223372036854775807
Wenn Sie beispielsweise die Obergrenze von "Integer" auf "+ 1" setzen, läuft sie über.
Integer.MAX_VALUE + 1; // -2147483648
Und natürlich ist das Gießen von weniger genau zu genauer in Ordnung, aber nicht umgekehrt.
short s = 20000;
(int) s; // 20000
int i = 40000;
(short) i; // -25536
Es unterscheidet sich vom ursprünglichen Zweck, ist aber unerwartet interessant, deshalb möchte ich es sagen.
Das Java int
wird als primitiver Typ und die Integer
als Klassentyp bezeichnet.
Die Hauptunterschiede sind grob gesagt, "int" erlaubt nicht "null" und "int" kann nicht "T" sein, wie "List
Außerdem verfügt Java über einen Mechanismus, mit dem der Compiler sich gegenseitig gut konvertieren kann, sodass Sie sich in den meisten Fällen auch keine Sorgen machen müssen.
Sie werden vielleicht nicht zu viel darüber nachdenken, aber ich werde den Stapelbereich und den Heap-Bereich sehr grob erklären.
Zum Beispiel, wenn Sie Code wie diesen schreiben. (Um die Unterscheidung zwischen "int" - und "Integer" -Variablen zu vereinfachen, ** werden in diesem Artikel am Anfang des Variablennamens Großbuchstaben verwendet.)
Integer Ia = new Integer(1);
In diesem Fall sieht der Speicher folgendermaßen aus.
Wenn Sie "neu" machen, wird etwas in die Variable "Ia" im Stapelbereich eingefügt. Irgendwie habe ich das Gefühl, dass "Ia" die Instanz selbst enthält, aber nur der ** Pfeil ** enthält sie. Auf beängstigende Weise ist es ein ** Zeiger **.
Die erstellte Instanz befindet sich im Heap-Bereich.
Andererseits ist der primitive Typ "int" so reserviert, wie er sich im Stapelbereich befindet.
Integer Ia = new Integer(1);
Integer Ib = new Integer(1);
int ia = 1;
int ib = 1;
Wenn Sie diesen Code schreiben, sieht das Bild wie folgt aus.
Ich bin mir sicher, dass es viele Leute gibt, die wütend auf gruselige Leute sind, die sagen: "Benutze ==
nicht zum Vergleich in Java ", aber mal sehen warum.
In einem Klassentyp ist "Identität" ** dieselbe Instanz ** und "Äquivalenz" ** der gleiche Wert **. Ersteres wird durch "==" und letzteres durch "gleich" ausgeführt. Die Gleichwertigkeit hängt auch von der Implementierung ab. (Beispielsweise kann beim Vergleich von DDD-Entitäten nur die Identitätsübereinstimmung als der gleiche Wert angesehen werden.)
Der primitive Typ ==
vergleicht einfach Werte.
Also ist Ia == Ib
** falsch **, weil es ein Pfeil mit einem anderen Ziel ist. Ia.equals (Ib)
ist ** true **, da die Zielwerte gleich sind.
Zum Beispiel "Herr A und Herr B haben beide 500 Yen Bälle, ** physikalisch unterschiedliche Münzen **, aber ** den gleichen Wert **".
Nachdem Sie den Stapelbereich, den Heap-Bereich und den Vergleich verstanden haben, geht es um gegenseitige Konvertierung.
int
-> Integer
heißt ** boxing ** und das Gegenteil heißt ** unboxing **.
Ich denke, es ist ein Bild, das in eine Wrapper-Klassenbox gelegt werden kann.
Der folgende Code kann durch ** Auto Boxing | Auto Unboxing ** ausgeführt werden.
Integer Ia = new Integer(1);
int ia = Ia; // unboxing
int ib = 1;
Integer Ib = ib; // boxing
Intern werden Werte in den Stapelbereich gebracht und Instanzen im Heap-Bereich erstellt, um Referenzen zu erhalten. (Eigentlich verschwindet der ursprüngliche Wert nicht, aber er ist dünn, weil er leicht vorstellbar ist.)
Welcher der folgenden Codes wäre nun "wahr" oder "falsch"?
int ia = 1;
int ib = 1;
Integer Ia = ia;
Integer Ib = ib;
Ia == Ib; // true or false ?
Die Pfeile "Ia" und "Ib" sollten unterschiedlich sein, da sie durch "Auto-Boxen" "neu" sind. Dies ist auch im obigen Bild der Fall.
Aber das ist "wahr".
Anscheinend wird ** Auto Boxing ** durch Integer # valueOf
und ** Auto Unboxing ** durch Integer # intValue
realisiert.
Integer Ia = Integer.valueOf(ia);
Integer Ib = Integer.valueOf(ib);
Das wesentliche "Integer # valueOf" ist also, aber es wird so implementiert.
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
Anscheinend scheint das häufig verwendete -128
~ 127
zwischengespeichert zu sein. Im obigen Codebeispiel ist es also nicht "neu".
Mit einem solchen Code wird es richtig "falsch" sein, es scheint, dass das Verständnis korrekt war und es sicher ist.
int ia = 1000;
int ib = 1000;
Integer Ia = ia;
Integer Ib = ib;
Ia == Ib; // false
Intern bedeutet die Verwendung von "Integer # intValue" für ** automatisches Entpacken ** übrigens, dass wenn "Ia" "null" ist, ** automatisches Entpacken ** zu "NullPointerException" führt.
Korrekt.
In diesem Artikel unterscheiden sich "int" und "Integer" sowie "float" und "Float" nicht in der Genauigkeit, daher verwenden wir die für Sie geeignete im Beispielcode ohne vorherige Ankündigung.
float, double Sie haben die Ganzzahl gedrückt. Als nächstes kommt der Bruch.
Ich habe mich gefragt, was das "Doppel" ist, aber es ist klar, wenn Sie studieren. Das "Float" verwendete 32 Bit und das "Double" 64 Bit, um den Wert darzustellen. Also doppelte Genauigkeit.
Da es unmöglich ist, "unendlichen Bruch" vollständig darzustellen, solange der Speicher des Computers endlich ist, muss davon ausgegangen werden, dass ein Fehler auftritt.
Beispielsweise kann die Dezimalzahl "0,01" nicht als endliche Zahl ausgedrückt werden, wenn es sich um eine Binärzahl handelt. Da es nicht als endliche Zahl ausgedrückt werden kann, müssen Sie irgendwo aufgeben, und wenn Sie es wiederholen, können Sie verstehen, dass der Fehler zunimmt.
Welche Art von Fehler wird also auftreten? Lass es uns versuchen.
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
ist näher an 1.0
.
Die Konvertierung zwischen "float" und "double" bricht wie "short" und "int" ab, wenn sie von der höheren Präzision in die niedrigere Präzision konvertiert wird.
f; // 0.99999934
d; // 1.0000000000000007
(double) f; // 0.9999993443489075
(float) d; // 1.0
Wenn Sie von "double" zu "float" wechseln, fehlt es.
Da es in erster Linie endlich ist, tritt bei den folgenden Werten einfach ein Fehler auf.
10d / 3d; // 3.3333333333333335
1.00000001f; // 1.0
BigInteger, BigDecimal Vielen Dank fürs Warten, die Jungs mit mehreren Längen.
Sie ordnen den Speicher dynamisch nach den Ziffern zu, sodass kein Überlauf und kein Fehler auftritt. Irgendwie erstaunlich.
Lass es uns gleich versuchen.
BigDecimal Versuchen Sie es mit der "großen Dezimalzahl" der "Fraktion". Lassen Sie uns von Anfang an großzügig mit einer riesigen Ganzzahl umgehen.
BigDecimal bd = new BigDecimal(Long.MAX_VALUE);
bd; // 9223372036854775807
bd.add(new BigDecimal(1)); // 9223372036854775808
Selbst wenn es zur Obergrenze von "long" hinzugefügt wird, läuft es nicht über. Es ist in Ordnung, mutiger hinzuzufügen.
bd.add(bd); // 18446744073709551614
Sie können auch "Brüche" hinzufügen.
bd.add(new BigDecimal(0.5)); // 9223372036854775807.5
Favorit? Was ist mit dem "Bruch" -Fehler von?
BigDecimal bd = BigDecimal.ZERO;
BigDecimal x = new BigDecimal(0.01);
for (int i = 0; i < 100; i++) {
bd = bd.add(x);
}
bd; // 1.00000000000000002081668171172168513294309377670288085937500
Es ist genauer als "1.0000000000000007" von "double". (ToString
wird gemacht, weil ich mein Bestes gebe.)
Was ist mit 10d / 3d
, das einen Fehler in double
hat?
BigDecimal bd10 = new BigDecimal(10);
BigDecimal bd3 = new BigDecimal(3);
bd10.divide(bd3); // ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
Ich habe das Wort "enden" in der Eröffnungsfigur von Ben gesehen und bin wütend, dass es kein endlicher Bruchteil ist.
Es scheint, dass der Wert mit dem Fehler nicht mit dem Fehler beibehalten wird. Es scheint nutzlos zu sein, wenn Sie nicht angeben, ob geschnitten oder aufgerundet werden soll.
bd10.divide(bd3, RoundingMode.FLOOR) // 3
bd10.divide(bd3, RoundingMode.CEILING) // 4
BigInteger Dieser Typ ist einfach. Es ist eine "große Dezimalzahl", die keine "Brüche" verarbeiten kann.
BigInteger bi = BigInteger.valueOf(Long.MAX_VALUE);
bi; // 9223372036854775807
bi.add(bi); // 18446744073709551614
BigInteger
hat keine Generierungsmethode, mit der Sie einen Bruch
wie 0.5
übergeben können, also ist es nur dies im Vergleich zu BigDecimal
.
Jetzt ist alles in Ordnung. Nicht beängstigend.
Übrigens, wenn Sie Lust auf Java haben, möchten Sie es nicht mit add
zerstören?
Es ist wie "List # add".
Wenn Sie jedoch verstehen, dass Sie den Speicher jedes Mal neu zuweisen können, wenn Sie ihn hinzufügen, können Sie sich leicht vorstellen, jedes Mal eine zerstörungsfreie, andere Instanz zu erstellen. (Es hängt von der Implementierungsmethode ab, kann also unveränderlich, aber veränderlich sein.)
Es war ein langer Artikel, aber es gibt nur drei Hauptpunkte des numerischen Ausdrucks in Java, die ich fühlte, nachdem ich es ausprobiert hatte!
BigInteger
und BigDecimal
sind unbegrenzt Integer
und Bruch
(solange Speicher vorhanden ist)Das ist es! Der Unterschied zwischen "int" und "Integer" besteht darin, dass ich mein Bestes geben werde, um Java und nicht den numerischen Ausdruck zu studieren!
Jedenfalls habe ich viel gelernt. Ich war mir sehr bewusst, wie gut ich normalerweise kam.
Wenn Sie dies verstehen, müssen Sie es von der Domänenlogik trennen. Erstellen Sie also ein Wertobjekt und verbergen Sie es! Ich verstehe es genau, deshalb benutze ich es nicht in meiner täglichen Arbeit (Domain-Implementierung)! Was für ein Paradoxon!
Recommended Posts