Numbers
Integer types
Kotlin provides a set of built-in types that represent numbers.
For integer numbers, there are four types with different sizes and value ranges:
Type | Size (bits) | Min value | Max value |
---|---|---|---|
| 8 | -128 | 127 |
| 16 | -32768 | 32767 |
| 32 | -2,147,483,648 (-231) | 2,147,483,647 (231 - 1) |
| 64 | -9,223,372,036,854,775,808 (-263) | 9,223,372,036,854,775,807 (263 - 1) |
When you initialize a variable with no explicit type specification, the compiler automatically infers the type with the smallest range enough to represent the value starting from Int
. If it doesn't exceed the range of Int
, the type is Int
. If it does exceed that range, the type is Long
. To specify the Long
value explicitly, append the suffix L
to the value. To use the Byte
or Short
type, specify it explicitly in the declaration. Explicit type specification triggers the compiler to check that the value doesn't exceed the range of the specified type.
Floating-point types
For real numbers, Kotlin provides floating-point types Float
and Double
that adhere to the IEEE 754 standard. Float
reflects the IEEE 754 single precision, while Double
reflects double precision.
These types differ in their size and provide storage for floating-point numbers with different precision:
Type | Size (bits) | Significant bits | Exponent bits | Decimal digits |
---|---|---|---|---|
| 32 | 24 | 8 | 6-7 |
| 64 | 53 | 11 | 15-16 |
You can initialize Double
and Float
variables only with numbers that have a fractional part. Separate the fractional part from the integer part by a period (.
)
For variables initialized with fractional numbers, the compiler infers the Double
type:
To explicitly specify the Float
type for a value, add the suffix f
or F
. If a value provided in this way contains more than 7 decimal digits, it is rounded:
Unlike in some other languages, there are no implicit widening conversions for numbers in Kotlin. For example, a function with a Double
parameter can be called only on Double
values, but not Float
, Int
, or other numeric values:
To convert numeric values to different types, use explicit conversions.
Literal constants for numbers
There are several kinds of literal constants for integral values:
Decimals:
123
Longs, ending with the capital
L
:123L
Hexadecimals:
0x0F
Binaries:
0b00001011
Kotlin also supports conventional notation for floating-point numbers:
Doubles (default when the fractional part does not end with a letter):
123.5
,123.5e10
Floats, ending with the letter
f
orF
:123.5f
You can use underscores to make number constants more readable:
Boxing and caching numbers on the Java Virtual Machine
The way the JVM stores numbers can make your code behave counterintuitively because of the cache used by default for small (byte-sized) numbers.
The JVM stores numbers as primitive types: int
, double
, and so on. When you use generic types or create a nullable number reference such as Int?
, numbers are boxed in Java classes such as Integer
or Double
.
The JVM applies a memory optimization technique to Integer
and other objects that represent numbers between −128
and 127
. All nullable references to such objects refer to the same cached object. For example, nullable objects in the following code are referentially equal:
For numbers outside this range, the nullable objects are different but structurally equal:
For this reason, Kotlin warns about using referential equality with boxable numbers and literals with the following message: "Identity equality for arguments of types ... and ... is prohibited."
When comparing Int
, Short
, Long
, and Byte
types (as well as Char
and Boolean
), use structural equality checks to get consistent results.
Explicit number conversions
Due to different representations, number types are not subtypes of each other. As a consequence, smaller types are not implicitly converted to bigger types and vice versa. For example, assigning a value of type Byte
to an Int
variable requires an explicit conversion:
All number types support conversions to other types:
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
toDouble(): Double
In many cases, there is no need for explicit conversion because the type is inferred from the context, and arithmetical operators are overloaded to handle conversions automatically. For example:
Reasoning against implicit conversions
Kotlin doesn't support implicit conversions because they can lead to unexpected behavior.
If numbers of different types were converted implicitly, we could sometimes lose equality and identity silently. For example, imagine if Int
was a subtype of Long
:
Operations on numbers
Kotlin supports the standard set of arithmetical operations over numbers: +
, -
, *
, /
, %
. They are declared as members of appropriate classes:
You can override these operators in custom number classes. See Operator overloading for details.
Division of integers
Division between integer numbers always returns an integer number. Any fractional part is discarded.
This is true for a division between any two integer types:
To return a division result with the fractional part, explicitly convert one of the arguments to a floating-point type:
Bitwise operations
Kotlin provides a set of bitwise operations on integer numbers. They operate on the binary level directly with bits of the numbers' representation. Bitwise operations are represented by functions that can be called in infix form. They can be applied only to Int
and Long
:
The complete list of bitwise operations:
shl(bits)
– signed shift leftshr(bits)
– signed shift rightushr(bits)
– unsigned shift rightand(bits)
– bitwise ANDor(bits)
– bitwise ORxor(bits)
– bitwise XORinv()
– bitwise inversion
Floating-point numbers comparison
The operations on floating-point numbers discussed in this section are:
Equality checks:
a == b
anda != b
Comparison operators:
a < b
,a > b
,a <= b
,a >= b
Range instantiation and range checks:
a..b
,x in a..b
,x !in a..b
When the operands a
and b
are statically known to be Float
or Double
or their nullable counterparts (the type is declared or inferred or is a result of a smart cast), the operations on the numbers and the range that they form follow the IEEE 754 Standard for Floating-Point Arithmetic.
However, to support generic use cases and provide total ordering, the behavior is different for operands that are not statically typed as floating-point numbers. For example, Any
, Comparable<...>
, or Collection<T>
types. In this case, the operations use the equals
and compareTo
implementations for Float
and Double
. As a result:
NaN
is considered equal to itselfNaN
is considered greater than any other element includingPOSITIVE_INFINITY
-0.0
is considered less than0.0
Here is an example that shows the difference in behavior between operands statically typed as floating-point numbers (Double.NaN
) and operands not statically typed as floating-point numbers (listOf(T)
).