I decided to read the JDK source somehow. That said, I don't have time to read each line carefully, so I read it briefly and found this code. Last time I read the source of Integer, so next will be Long.
The Long class is a wrapper class for the primitive type long. First, the fields and the constructor. Well, it's a source that everyone can imagine.
Long.java
private final long value;
public Long(long value) {
this.value = value;
}
Actually, there is a LongCache class that cannot be seen in javadoc.
Long.java
private static class LongCache {
private LongCache(){}
static final Long cache[] = new Long[-(-128) + 127 + 1];
static {
for(int i = 0; i < cache.length; i++)
cache[i] = new Long(i - 128);
}
}
If I think that Long is also an external parameter like Integer, it was fixed from -128 to 127. I didn't quote it without the javadoc comment, and I didn't write anything originally. It's been the case since JDK 1.5, so Oracle doesn't seem to be relevant.
It is a cache of LongCache, but it is referenced by valueOf.
Long.java
public static Long valueOf(long l) {
final int offset = 128;
if (l >= -128 && l <= 127) { // will cache
return LongCache.cache[(int)l + offset];
}
return new Long(l);
}
I thought I'd compare it with the comparison operator ==, but it's the same as Byte, Short, and Integer.
It is also in Integer. The functional ones were the same when I thought they were together, but the implementation was slightly different. It's natural because it's different from 32-bit and 64-bit.
Integer.java
public static int bitCount(int i) {
// HD, Figure 5-2
i = i - ((i >>> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
i = (i + (i >>> 4)) & 0x0f0f0f0f;
i = i + (i >>> 8);
i = i + (i >>> 16);
return i & 0x3f;
}
Long.java
public static int bitCount(long i) {
// HD, Figure 5-14
i = i - ((i >>> 1) & 0x5555555555555555L);
i = (i & 0x3333333333333333L) + ((i >>> 2) & 0x3333333333333333L);
i = (i + (i >>> 4)) & 0x0f0f0f0f0f0f0f0fL;
i = i + (i >>> 8);
i = i + (i >>> 16);
i = i + (i >>> 32);
return (int)i & 0x7f;
}
The constants change because the bit widths are different. Well, Figure 5-2 and Figure 5-14. Either one is wrong. If you would pay $ 2.56 to a typo like Mr. Knuth, I would look for it thoroughly.
There were sum, max, min added from JDK 1.8 that don't feel the same value as Integer. I didn't think I skipped Byte and Short.
Long.java
public static long sum(long a, long b) {
return a + b;
}
public static long max(long a, long b) {
return Math.max(a, b);
}
public static long min(long a, long b) {
return Math.min(a, b);
}
I wrote it in Integer, but if sum is a method with overflow detection function, it still makes sense. For example, I thought it would be professional if there was a long overflow detection with multiplication instead of sum. Overflow detection can be calculated with long by multiplying int, but overflow detection by multiplying long is troublesome.
I'm sorry that it casually says @see java.util.function.BinaryOperator. I don't know how they are related.
Constants are defined for Byte, Short, Integer, and Long. There are five, MIN_VALUE, MAX_VALUE, TYPE, SIZE, BYTES, and the first three are from ancient times, with SIZE added from JDK 1.5 and BYTES from JDK 1.8. Well, I only use MIN_VALUE and MAX_VALUE.
Byte.java
public static final byte MIN_VALUE = -128;
public static final byte MAX_VALUE = 127;
Short.java
public static final short MIN_VALUE = -32768;
public static final short MAX_VALUE = 32767;
Integer.java
@Native public static final int MIN_VALUE = 0x80000000;
@Native public static final int MAX_VALUE = 0x7fffffff;
Long.java
@Native public static final long MIN_VALUE = 0x8000000000000000L;
@Native public static final long MAX_VALUE = 0x7fffffffffffffffL;
It is a source that makes it easy for humans to understand where a large number is recognized. I wish I had all hexadecimal numbers. However, when I looked at the source, I wondered what the maximum value of int and long should be in decimal, and it turned out to be Ghan. Looking at the javadoc, there are constant field values, but they are 2147483647 and 9223372036854775807L.
Hash calculation logic has been implemented in static methods since JDK 1.8. Until JDK 1.7, the logic was written in normal methods.
Long.java
public int hashCode() {
return Long.hashCode(value);
}
public static int hashCode(long value) {
return (int)(value ^ (value >>> 32));
}
When thinking about how to process 64 bits into 32 bits, the upper and lower 32 bits were exclusive OR (XOR).
This is an old method.
Long.java
public static String toString(long i) {
if (i == Long.MIN_VALUE)
return "-9223372036854775808";
int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
char[] buf = new char[size];
getChars(i, size, buf);
return new String(buf, true);
}
The processing at the time of Long.MIN_VALUE is quite difficult. Well, if it is negative, the absolute value will be larger by 1, so it will be troublesome in various ways. Actually, I make a string with getChars, but it is a path because it is troublesome to look at the source.
If you think about what Integer.toString is,
Integer.java
public static String toString(int i) {
if (i == Integer.MIN_VALUE)
return "-2147483648";
int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
char[] buf = new char[size];
getChars(i, size, buf);
return new String(buf, true);
}
What are you doing too? So what about Short and Byte?
Short.java
public static String toString(short s) {
return Integer.toString((int)s, 10);
}
Byte.java
public static String toString(byte b) {
return Integer.toString((int)b, 10);
}
As expected, these were calling Integer.toString.
This is an old method.
Long.java
public static long parseLong(String s, int radix)
throws NumberFormatException
{
if (s == null) {
throw new NumberFormatException("null");
}
if (radix < Character.MIN_RADIX) {
throw new NumberFormatException("radix " + radix +
" less than Character.MIN_RADIX");
}
if (radix > Character.MAX_RADIX) {
throw new NumberFormatException("radix " + radix +
" greater than Character.MAX_RADIX");
}
long result = 0;
boolean negative = false;
int i = 0, len = s.length();
long limit = -Long.MAX_VALUE;
long multmin;
int digit;
if (len > 0) {
char firstChar = s.charAt(0);
if (firstChar < '0') { // Possible leading "+" or "-"
if (firstChar == '-') {
negative = true;
limit = Long.MIN_VALUE;
} else if (firstChar != '+')
throw NumberFormatException.forInputString(s);
if (len == 1) // Cannot have lone "+" or "-"
throw NumberFormatException.forInputString(s);
i++;
}
multmin = limit / radix;
while (i < len) {
// Accumulating negatively avoids surprises near MAX_VALUE
digit = Character.digit(s.charAt(i++),radix);
if (digit < 0) {
throw NumberFormatException.forInputString(s);
}
if (result < multmin) {
throw NumberFormatException.forInputString(s);
}
result *= radix;
if (result < limit + digit) {
throw NumberFormatException.forInputString(s);
}
result -= digit;
}
} else {
throw NumberFormatException.forInputString(s);
}
return negative ? result : -result;
}
Checking NumberFormatException is fine. There is also a + only-only check.
If you think it's okay to take a quick look, are you pulling with result-= digit ;? If you think it's an addition, return negative? Result: -result; and the sign is opposite. I wonder if the minus is because the absolute value is 1 larger.
If you wonder what if (result <limit + digit) is, do you do result -digit and bring it to the right side to check the range so that the absolute value does not overflow? If I write it myself, I will pull it for the time being, and if the value that should be negative becomes positive, it will be judged as overflow.
A method called parseUnsignedInt has been added to Integer since JDK 1.8, but it has also been added to Long. Isn't java long signed? What does it mean to parse it unsigned?
Long.java
public static long parseUnsignedLong(String s, int radix)
throws NumberFormatException {
if (s == null) {
throw new NumberFormatException("null");
}
int len = s.length();
if (len > 0) {
char firstChar = s.charAt(0);
if (firstChar == '-') {
throw new
NumberFormatException(String.format("Illegal leading minus sign " +
"on unsigned string %s.", s));
} else {
if (len <= 12 || // Long.MAX_VALUE in Character.MAX_RADIX is 13 digits
(radix == 10 && len <= 18) ) { // Long.MAX_VALUE in base 10 is 19 digits
return parseLong(s, radix);
}
// No need for range checks on len due to testing above.
long first = parseLong(s.substring(0, len - 1), radix);
int second = Character.digit(s.charAt(len - 1), radix);
if (second < 0) {
throw new NumberFormatException("Bad digit at end of " + s);
}
long result = first * radix + second;
if (compareUnsigned(result, first) < 0) {
/*
* The maximum unsigned value, (2^64)-1, takes at
* most one more digit to represent than the
* maximum signed value, (2^63)-1. Therefore,
* parsing (len - 1) digits will be appropriately
* in-range of the signed parsing. In other
* words, if parsing (len -1) digits overflows
* signed parsing, parsing len digits will
* certainly overflow unsigned parsing.
*
* The compareUnsigned check above catches
* situations where an unsigned overflow occurs
* incorporating the contribution of the final
* digit.
*/
throw new NumberFormatException(String.format("String value %s exceeds " +
"range of unsigned long.", s));
}
return result;
}
} else {
throw NumberFormatException.forInputString(s);
}
}
Anyway, if you reduce it by one digit and parseLong, and if it decreases when you add the last digit, it feels like an overflow. It parses unsigned, but it can be negative. It's a great specification.
Let's try it.
Main.java
public static void main(String[] args) {
long l01 = Long.parseUnsignedLong("9223372036854775807");
System.out.println(l01);
long l02 = Long.parseUnsignedLong("9223372036854775808");
System.out.println(l02);
long l03 = Long.parseUnsignedLong("18446744073709551615");
System.out.println(l03);
long l04 = Long.parseUnsignedLong("18446744073709551616");
System.out.println(l04);
}
When you run ...
9223372036854775807
-9223372036854775808
-1
Exception in thread "main" java.lang.NumberFormatException: String value 18446744073709551616 exceeds range of unsigned long.
at java.lang.Long.parseUnsignedLong(Long.java:719)
at java.lang.Long.parseUnsignedLong(Long.java:746)
at Main.main(Main.java:28)
The result is as expected, but are you happy? this. If you want to use it 64-bit full, you can use BigInteger.
In the past, 32-bit was the standard, so it's understandable that 32-bit ints and 64-bit longs are implemented separately. If it is long, the processing seems to be heavy. However, 64-bit VMs are generally used in 64-bit OSs, and nowadays, I feel that 64-bit longs work normally in terms of hardware. For example, on Intel's x64, the general purpose register is 8-bit register: AH AL BH BL CH CL DH DL 16-bit register: AX BX CX DX 32-bit register: EAX EBX ECX EDX 64-bit register: RAX RBX RCX RDX R8 R9 R10 R11 R12 R13 R14 R15 Now, the number of 64-bit registers is increasing so much!
I feel that the bytecode specification of the current JVM is based on 32-bit assumptions, so I think it will be possible to upgrade it to 64-bit assumptions and make long long 128-bit.
Recommended Posts