[JAVA] I read the source of Long

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.

Long class

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;
    }

LongCache class

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.

Hacker's Delight source

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.

sum method, max method, min method

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.

Constant

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.

hashCode method

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).

toString method

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.

parseLong method

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.

parseUnsignedLong method

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.

Finally

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

I read the source of Long
I read the source of ArrayList I read
I read the source of Integer
I read the source of Short
I read the source of Byte
I read the source of String
I read the Kotlin startbook
Is drainTo of LinkedBlockingQueue safe? I followed the source
05. I tried to stub the source of Spring Boot
I investigated the internal processing of Retrofit
[day: 5] I summarized the basics of Java
Read the Perelman treatise of Poincare conjecture
I want to output the day of the week
I understood the very basics of character input
I compared the characteristics of Java and .NET
I want to var_dump the contents of the intent
I touched on the new features of Java 15
I tried using the profiler of IntelliJ IDEA
I checked the number of taxis with Ruby
Try the free version of Progate [Java I]
[Java] How to get the URL of the transition source
I tried using the Server Push function of Servlet 4.0
I read the readable code, so make a note
I was addicted to the record of the associated model
I tried to summarize the state transition of docker
I saw the list view of Android development collectively
I tried to reduce the capacity of Spring Boot
Judgment of the calendar
I tried the new feature profiler of IntelliJ IDEA 2019.2.
The world of clara-rules (4)
I want to know the answer of the rock-paper-scissors app
Image processing: The basic structure of the image read by the program
I want to display the name of the poster of the comment
I summarized the display format of the JSON response of Rails
The world of clara-rules (1)
The world of clara-rules (3)
Read Java HashMap source
I read the "Object-Oriented Practical Guide", so a memorandum
Source of cellular objects
Read the Rails Guide (Overview of Action Controller) again
[Java] When writing the source ... A memorandum of understanding ①
I wrote a sequence diagram of the j.u.c.Flow sample
The world of clara-rules (5)
I summarized the types and basics of Java exceptions
The idea of quicksort
[WIP] I tried the configuration of Docker + Streama + NFS
I am keenly aware of the convenience of graphql-code-generator, part 2
I can't get out of the Rails dbconsole screen
I learned about the existence of a gemspec file
I want to be aware of the contents of variables!
I want to return the scroll position of UITableView!
The idea of jQuery
I made the server side of an online card game ①
I didn't understand the behavior of Java Scanner and .nextLine ().
I took a peek at the contents of Java's HashMap
I tried to summarize the basics of kotlin and java
Now, I understand the coordinate transformation method of UIView (Swift)
[Android] Exit the activity of the transition source at the time of screen transition
I want to expand the clickable part of the link_to method
I want to change the log output settings of UtilLoggingJdbcLogger
[Swift] I tried to implement the function of the vending machine