Skip to content
Open
7 changes: 7 additions & 0 deletions concepts/numbers/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"authors": [
"colinleach"
],
"contributors": [],
"blurb": "Kotlin has a variety of integer and floating point types, and a math library to manipulate them."
}
148 changes: 148 additions & 0 deletions concepts/numbers/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# About Numbers

## Numerical types

[Numbers][numbers] can be integer, unsigned integer, or floating point types.
Each comes in various "sizes", meaning how many bits it needs in memory.

- Integers can be `Byte`, `Short`, `Int` or `Long`, respectively 8, 16, 32 and 64 bits.
- Unsigned integers have a `U` prefix: `UByte`, `UShort`, `UInt` or `ULong`.
- Floating point types are `Float` (32-bit) or `Double` (64-bit).

Integer variables relying on type inference default to `Int`, even on 64-bit machines, but floating point variables default to `Double`.

Other types can, of course, be specified, but there are a few syntactic shortcuts, and big integer literals become `Long` if they would overflow `Int`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes sense to quickly explain what 8-64 bits mean and what overflow means. Many languages do NOT have fixed-width numeric types. In Ruby for example, every number is arbitrary precision.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a paragraph - is this what you wanted?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revised and expanded again.

Copy link
Member

@SleeplessByte SleeplessByte Sep 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better, but assume someone doesn't know what "number can be x bytes" means. I am a strong opponent to "just" explain this, especially in about.md, but also in introduction.md if it's relevant. We could add above line 16 something like:

In Kotlin, like many strongly types languages, numbers take a fixed amount of space in memory, which also means for each numeric type there is a smallest and largest value that fits.
This is generally expressed in bits or bytes.

For numeric types that can go below and above zero, one bit is reserved to indicate the *sign*.
This effectively cuts the amount of integers that fits in the memory in half, and these are called signed integers.
In contrast, there are also unsigned integers which do not reserve a bit for the sign, doubling the maximum value the memory for that number can hold.

You may want to reword in your own words.

Sidenote: if you feel my or kahgoh's contributions are significant, feel free to add us to the contributors array when you see fit.


```Kotlin
val one = 1 // defaults to Int
val threeBillion = 3_000_000_000 // Long, with optional underscores for clarity
val oneLong = 1L // Long
val oneByte: Byte = 1
val oneDouble = 1.0 // defaults to Double
val oneFloat = 1.0f //Float
val lightSpeed = 3.0e8 // scientific notation (units of m/s)
```

Hexadecimal and binary literals are conventional: `0x7F` and `0b100101` respectively.
Octal literals are not supported in Kotlin.

Copy link
Member

@SleeplessByte SleeplessByte Sep 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe also add the Kotlin does not support arbitrary precision arithmetic in its standard library, so a userland solution is necessary for BigInteger and BigDecimal support?

It just doesn't exist for multiplatform, but does exist for JVM...

## Arithmetic

The basic arithmetic operators are the same as in many languages:

```Kotlin
4 + 3 // => 7
4 - 3 // => 1
4 * 3 // => 12
4 / 3 // => 1 Int / Int always gives an Int
-8 / 3 // => -2 Truncated towards zero
-8.0 / 3 // => -2.6666666666666665
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
4 / 3 // => 1 Int / Int always gives an Int
-8 / 3 // => -2 Truncated towards zero
-8.0 / 3 // => -2.6666666666666665
12 / 4 => 3
```
When dividing an integer (for example an Int) by another integer (for example an Int), the result will also be an integer (in this case an Int):
```kotlin
4 / 3 // => 1 Int / Int always gives an Int
-8 / 3 // => -2 Truncated towards zero
```

```

To get a floating point result from division, at least one of the numerator / denominator must be floating point.

Division by zero is more interesting.

```Kotlin
1 / 0 // => java.lang.ArithmeticException: / by zero
3.0 / 0.0 // => Infinity
0.0 / 0.0 // => NaN (Not a Number)
```

Integer division by zero is an error, but [IEEE floating point standards][IEEE] can apply in other cases.

The modulo operator `%` gives the remainder from integer division:

```Kotlin
8 % 3 // => 2
```

Kotlin, like other JVM languages, has no exponentiation operator (_this annoys scientists and engineers_).
We need to use the [`pow()`][pow] function from the [`math`][math] library, and the number being raised to some power must be `Float` or `Double`.

```kotlin
2.0.pow(3) // => 8.0
2.pow(3) // Unresolved reference (2 is Int, so not allowed)
```

## Rounding

The [`math`][math] library contains several functions to round floating point numbers to a nearby integer.

_Did the last line sound slightly odd?_
When we say "a nearby integer", there are two questions:

- Is the return value still Float/Double, or is it converted to Int/Long?
- How are ties rounded? Does `4.5` round to `4` or `5`?

It is not Kotlin's fault if this seems complicated.
Mathematicians have been arguing about this for centuries.

### `round()`, `floor()`, `ceil()`, `truncate()`

These four functions all return the same floating-point type as the input, but differ in how they round.

- [`round()`][round] gives the _nearest_ integer if this is unambiguous, or the _nearest even_ integer when tied.
- [`floor()`][floor] rounds towards negative infinity.
- [`ceil()`][ceil] rounds towards positive infinity
- [`truncate()`][truncate] rounds towards zero

```kotlin
round(4.7) // => 5.0 (nearest integer)
round(4.5) // => 4.0 (nearest even integer)

floor(4.7) // => 4.0 (towards -Inf)
floor(-4.7) // => 5.0

ceil(4.7) // => 5.0 (towards +Inf)
ceil(-4.7) // => -4.0

truncate(4.7) // => 4.0 (towards zero)
truncate(-4.7) // => -4.0
```

### `roundToInt()`, `roundToLong()`

These two functions do a type conversion after rounding, so the return type is `Int` or `Long` respectively.

Ties are always rounded towards positive infinity.

```kotlin
4.3.roundToInt() // => 4 (nearest integer)
4.5.roundToInt() // => 5 (nearest integer towards +Inf)
(-4.5).roundToInt() // => -4
```

Note also the different syntax: `x.roundToInt()` versus `round(x)`.

## Type conversions

Kotlin will quietly do implicit conversions in a few cases, for example `Int` to `Double` in a mixed arithmetic expression:

```Kotlin
3 + 4.0 // => 7.0
```

Internally, the `+` operator is overloaded to handle this.

More generally, explicit conversions are required:

```Kotlin
val x = 7.3
x.toInt() // => 7

val n = 42
n.toDouble() // => 42.0
```

See the [manual][conversions] for the full list of `toX()` methods.

[numbers]: https://kotlinlang.org/docs/numbers.html
[IEEE]: https://en.wikipedia.org/wiki/IEEE_754
[conversions]: https://kotlinlang.org/docs/numbers.html#explicit-number-conversions
[pow]: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.math/pow.html
[math]: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.math/
[round]: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.math/round.html
[floor]: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.math/floor.html
[ceil]: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.math/ceil.html
[truncate]: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.math/truncate.html
140 changes: 140 additions & 0 deletions concepts/numbers/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# Introduction

## Numerical types

[Numbers][numbers] can be integer, unsigned integer, or floating point types.
Each comes in various "sizes", meaning how many bits it needs in memory.

- Integers can be `Byte`, `Short`, `Int` or `Long`, respectively 8, 16, 32 and 64 bits.
- Unsigned integers have a `U` prefix: `UByte`, `UShort`, `UInt` or `ULong`.
- Floating point types are `Float` (32-bit) or `Double` (64-bit).

Integer variables relying on type inference default to `Int`, even on 64-bit machines, but floating point variables default to `Double`.

Other types can, of course, be specified, but there are a few syntactic shortcuts, and big integer literals become `Long` if they would overflow `Int`.

```Kotlin
val one = 1 // defaults to Int
val threeBillion = 3_000_000_000 // Long, with optional underscores for clarity
val oneLong = 1L // Long
val oneByte: Byte = 1
val oneDouble = 1.0 // defaults to Double
val oneFloat = 1.0f //Float
val lightSpeed = 3.0e8 // scientific notation (units of m/s)
```

Hexadecimal and binary literals are conventional: `0x7F` and `0b100101` respectively.
Octal literals are not supported in Kotlin.

## Arithmetic

The basic arithmetic operators are the same as in many languages:

```Kotlin
4 + 3 // => 7
4 - 3 // => 1
4 * 3 // => 12
4 / 3 // => 1 Int / Int always gives an Int
-8 / 3 // => -2 Truncated towards zero
-8.0 / 3 // => -2.6666666666666665
```

To get a floating point result from division, at least one of the numerator / denominator must be floating point.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this true for any arithmetic operator in Kotlin? For example 3 + 2.0 will give 5.0 and 3 * 2.0 gives 6.0 (both results are doubles).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True. However, only integer division causes large rounding errors. I'll try to make the wording clearer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To get a floating point result, at least one of the numbers in the calculation must be floating point. This is particularly significant for division, as integer division often leads to truncation.

Is this better?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wondering if it's worth explaining that Kotlin works out the result number types in the calculations. I'll try suggest something in a separate comment.


Division by zero is more interesting.

```Kotlin
1 / 0 // => java.lang.ArithmeticException: / by zero
3.0 / 0.0 // => Infinity
0.0 / 0.0 // => NaN (Not a Number)
```

Integer division by zero is an error, but IEEE floating point standards can apply in other cases.

The modulo operator `%` gives the remainder from integer division:

```Kotlin
8 % 3 // => 2
```

Kotlin, like other JVM languages, has no exponentiation operator.
We need to use the `pow()` function from the [`math`][math] library, and the number being raised to some power must be `Float` or `Double`.

```kotlin
2.0.pow(3) // => 8.0
2.pow(3) // Unresolved reference (2 is Int, so not allowed)
```

## Rounding

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@colinleach I wasn't quite sure if you meant to remove bits from this introduction as well or just from the exercise's introduction in the comment (ignore this if you meant just from the exercise introduction), but if you're looking for something to cut or separate out, the Cars Assemble exercise dosen't cover rounding and the use of the math library.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you make a cut-down version in the exercise, I'll wait till that is merged then copy it back to the concept. Probably easier than trying to keep them in sync as yours goes through review and editing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what to do about the math library. I covered rounding here because some other tracks do (with an exercise that needs it such as python/currency-exchange). If this was a more mathy track (like Julia or R), a separate math concept might be appropriate - probably not for Kotlin.

I think I'll leave it in the About for now. If any of the later exercises need rounding, we can move this section to there.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leave it in about, we can remove from introduction as we build the track out. Moving content between concepts is far easier than adding new content!

The [`math`][math] library contains several functions to round floating point numbers to a nearby integer.

_Did the last line sound slightly odd?_
When we say "a nearby integer", there are two questions:

- Is the return value still Float/Double, or is it converted to Int/Long?
- How are ties rounded? Does `4.5` round to `4` or `5`?

It is not Kotlin's fault if this seems complicated.
Mathematicians have been arguing about this for centuries.

### `round()`, `floor()`, `ceil()`, `truncate()`

These four functions all return the same floating-point type as the input, but differ in how they round.

- `round()` gives the _nearest_ integer if this is unambiguous, or the _nearest even_ integer when tied.
- `floor()` rounds towards negative infinity.
- `ceil()` rounds towards positive infinity
- `truncate()` rounds towards zero

```kotlin
round(4.7) // => 5.0 (nearest integer)
round(4.5) // => 4.0 (nearest even integer)

floor(4.7) // => 4.0 (towards -Inf)
floor(-4.7) // => 5.0

ceil(4.7) // => 5.0 (towards +Inf)
ceil(-4.7) // => -4.0

truncate(4.7) // => 4.0 (towards zero)
truncate(-4.7) // => -4.0
```

### `roundToInt()`, `roundToLong()`

These two functions do a type conversion after rounding, so the return type is `Int` or `Long` respectively.

Ties are always rounded towards positive infinity.

```kotlin
4.3.roundToInt() // => 4 (nearest integer)
4.5.roundToInt() // => 5 (nearest integer towards +Inf)
(-4.5).roundToInt() // => -4
```

Note also the different syntax: `x.roundToInt()` versus `round(x)`.

## Type conversions

Kotlin will quietly do implicit conversions in a few cases, for example `Int` to `Double` in a mixed arithmetic expression:

```Kotlin
3 + 4.0 // => 7.0
```

More generally, explicit conversions are required:

```Kotlin
val x = 7.3
x.toInt() // => 7

val n = 42
n.toDouble() // => 42.0
```

See the [manual][conversions] for the full list of `toX()` methods.

[numbers]: https://kotlinlang.org/docs/numbers.html
[conversions]: https://kotlinlang.org/docs/numbers.html#explicit-number-conversions
[math]: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.math/
14 changes: 14 additions & 0 deletions concepts/numbers/links.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"url": "https://kotlinlang.org/docs/numbers.html",
"description": "Kotlin introduction to numbers"
},
{
"url": "https://kotlinlang.org/docs/numbers.html#explicit-number-conversions",
"description": "Numeric type conversions"
},
{
"url": "https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.math/",
"description": "Kotlin math library"
}
]
5 changes: 5 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -1387,6 +1387,11 @@
"uuid": "168827c0-4867-449a-ad22-611c87314c48",
"slug": "conditionals",
"name": "Conditionals"
},
{
"uuid": "0501f1e3-8443-4386-9107-4c28cb10469f",
"slug": "numbers",
"name": "Numbers"
}
],
"key_features": [
Expand Down