Int and long integer types
Programmers often use integer numbers with their programs. As you may recall, Kotlin has several integer types. The most popular are Int (32 bits) and Long (64 bits). You can perform all types of arithmetic operations (+, -, *, /, %) with variables of these types. Let's take a look at several examples.
val two = 2 // Int
val ten = 10 // Int
val twelve = two + ten // 12
val eight = ten - two // 8
val twenty = two * ten // 20
val five = ten / two // 5
val zero = ten % two // 0, no remainder
You know what these operations do. Long type integers support all these operations as well. To declare a variable of the Long type, you may choose one of these ways:
val longNumber1 = 1_000_000_000_000_000
val longNumber2: Long = 1_000_000
val longNumber3 = 1000L
val result = longNumber1 + longNumber2 - longNumber3
println(result) // 1000000000999000
The longNumber1 value is of the Long type because it is greater than the possible value of the Int type. The longNumber2 value is also Long because we have specified the type. The longNumber3 value is Long because we have tagged the value with the suffix L (Long). The result is Long, as it is the sum of three Long integers.
Tip: Use Long integers only when it is absolutely necessary (for example, to process large values).
See the underscores _ in the example above? In Kotlin, you can use underscores to improve the readability of a number. We can group digits with them. Underscores cannot be placed at the start or end of a number, but they can be used within an integer (between digits), including consecutively.
Reading numbers from the standard input
To solve a problem, you generally need to read data from the input, process it, and output the result. For example, this program reads two numbers from the standard input, performs addition, and outputs the sum.
fun main() {
val a = readln().toInt()
val b = readln().toInt()
val sum = a + b
println(sum)
}
The readln() part is responsible for reading data. It works for positive, negative, and zero numbers because the Int type supports all of them.
If we know that input numbers can be large, we can read Longs instead of Ints:
val a = readln().toLong()
val b = readln().toLong()
After that, we don't need to change anything.
Unsigned integers
We already know that all integer types in Kotlin — Int, Long, Byte, and Short can be both positive and negative. In addition to all these integer types, Kotlin provides the ability to create unsigned integers – integers that can contain only non-negative values.
Kotlin provides the following unsigned types:
Type | Description |
| an unsigned 8-bit integer, ranges from 0 to 255 |
| an unsigned 16-bit integer, ranges from 0 to 65535 |
| an unsigned 32-bit integer, ranges from 0 to 4 294 967 295 (2^32-1) |
| an unsigned 64-bit integer, ranges from 0 to 18 446 744 073 709 551 615 (2^64-1) |
Unsigned numbers are created in the same way as any others. In order to indicate that you are creating an unsigned number, you need to add the suffix "u" or "U" to it.
val uByte: UByte = 5u
val uShort: UShort = 10U
In this example, we create variables of specified types. But, if you don't indicate the type directly, then the compiler will use UInt or ULong depending on the size of the literal:
val smallSize = 100u // UInt by default
val bigSize = 5_000_000_000u // ULong because the number doesn't fit in UInt
There is also a special suffix "uL" (or "UL"). If you tag a number with this suffix, then an ULong will be created regardless of the size of the number:
val smallLong = 10uL // ULong because it is marked with "uL"Data type overflow
All arithmetic operations for signed types are possible with their unsigned counterparts, except for the unary minus operation.
Let's look at the results of the following code:
// MAX_VALUE: Int = 2147483647
var d: Int = 2147483647
d += 1
println(d) // -2147483648
An unexpected result. A similar situation is called data type overflow. Note that these are details and it is not necessary to figure it out now. To understand what happened, imagine an empty glass into which water is poured. When the glass is full, the water overflows over the edges. The same situation happens with variables. When the variable value is close to the boundary value, there is a risk of a situation where the resulting value does not fit into the allocated memory for the variable. Overflow of the variable type leads to data loss, unexpected behavior of the program, etc.
Let's figure out why the overflow occurred in our example. Variables of type Int have a maximum value of MAX_VALUE: Int = 2147483647. Let's see what happens when the number increases more than the maximum value. We remember that the computer works only 1 and 0. For a computer, the number 2147483647 is as follows:
2147483647 = 01111111111111111111111111111111
When we add 1 to this number, we get the following result:
10000000000000000000000000000000 = -2147483648
All magic occurs when the left bit of the number takes the value 1 and the number takes a negative value. You can study this topic in more detail here, here, and here. Now we can say when we have reached the maximum value of the data type, the next value after the maximum will be the minimum value of the type. And so in a circle.
Let's look at another example:
val c: Long = 1_000_000_000_000_000
println(c.toInt())
Here is the result of the code execution:
-1530494976
In this example, we tried to put the Long variable in Int. A cut occurred, the lower bits of the number remained and the higher bits were lost, the result is again unpredictable. Look at how this happens:
It is important to remember the following points:
Data type overflow errors are programmer errors.
Program behavior in the case of data type overflow is unpredictable.
The compiler will not warn you for type overflows, so you need to correctly select data types for variables, as well as carefully monitor the transformation of data types.
Conclusion
In this topic, we have covered two basic Kotlin integer types — Long and Int, and learned how to read data from the input. We've also learned what unsigned integer types are. Now you have enough skills to write programs that process data. You may use the template above to solve the code problems in this topic. Remember to give meaningful names to your variables, as it really helps.