Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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."
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
"blurb": "Kotlin has a variety of integer and floating point types, and a math library to manipulate them."
"blurb": "Kotlin has a variety of integer and floating point types, and a math library to manipulate them."

Copy link
Contributor

Choose a reason for hiding this comment

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

I'll make changes in PR #711 (I can't edit this PR)

}
154 changes: 154 additions & 0 deletions concepts/numbers/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# 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.

Unlike some scripting languages (Ruby, recent versions of Python), each type in Kotlin has a maximum ([`MAX_VALUE`][max_value]) and minimum ([`MIN_VALUE`][min_value]) value it can store.
Assigning larger values will cause ["overflow"][wiki-overflow], causing either an exception (_bad_) or corrupted data (_worse_).

- Integers can be `Byte`, `Short`, `Int` or `Long`, respectively 8, 16, 32 and 64 bits (1, 2 4, 8 bytes).
- Unsigned integers have a `U` prefix: `UByte`, `UShort`, `UInt` or `ULong`.
Copy link
Member

Choose a reason for hiding this comment

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

We should explain signed versus unsigned at some point before this.

Copy link
Contributor

Choose a reason for hiding this comment

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

There isn't much "before", so I tried to clarify these bullets.

Copy link
Member

Choose a reason for hiding this comment

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

I am now reviewing this and I saw that is a suggestion I already made on the concept doc.

- 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`.
Copy link
Member

Choose a reason for hiding this comment

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

Would students be expected to know what type inference is by this point?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, it was in Basics


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.
Copy link
Member

@BNAndras BNAndras Aug 31, 2025

Choose a reason for hiding this comment

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

conventional relative to what? What makes them a hexadecimal or binary literal specifically?

Copy link
Member

Choose a reason for hiding this comment

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

I am now reviewing this and I saw that is a suggestion I already made on the concept doc.

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.

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][wiki-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
[wiki-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
[wiki-overflow]: https://en.wikipedia.org/wiki/Integer_overflow
[max_value]: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-int/-companion/#-244053257%2FProperties%2F-956074838
[min_value]: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-int/-companion/#-1907397559%2FProperties%2F-956074838
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.

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

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"
}
]
21 changes: 19 additions & 2 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,18 @@
"basics"
],
"status": "wip"
},
{
"slug": "cars-assemble",
"name": "cars-assemble",
"uuid": "9a67e10e-c331-4474-af3a-9cea17aa3f87",
"concepts": [
"numbers"
],
"prerequisites": [
"basics"
],
"status": "wip"
}
],
"practice": [
Expand Down Expand Up @@ -1196,15 +1208,15 @@
"practices": [],
"prerequisites": [],
"difficulty": 6,
"status": "deprecated",
"topics": [
"conditionals",
"games",
"integers",
"lists",
"matrices",
"strings"
],
"status": "deprecated"
]
},
{
"slug": "rail-fence-cipher",
Expand Down Expand Up @@ -1387,6 +1399,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
25 changes: 25 additions & 0 deletions exercises/concept/cars-assemble/.docs/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Hints

## General

- To learn more about type conversions in Kotlin, check [Explicit number conversions][type conversions].

## 1. Calculate the number of working cars produced per hour

- The percentage (passed as an argument) is a number between 0-100. To make this percentage a bit easier to work with, start by dividing it by 100.
- To compute the number of cars produced successfully, multiply the percentage (divided by 100) by the number of cars produced.

## 2. Calculate the number of working cars produced per minute

- Start by calculating the production of successful cars per hour. For this, you can use the function you implemented from the previous step.
- Knowing the production per hour of cars, you can get the production per minute by dividing the production per hour by 60 (the number of minutes in an hour).
- Remember to cast the result to an `Int`

## 3. Calculate the cost of production

- Start by working out how many groups of 10 cars there are. You can do this by dividing the number of cars by 10.
- Then work out how many cars are remaining (the [modulo operator][modulo operator] is useful for this).
- To arrive at the cost, multiply the number of groups by the cost to produce 10 cars and then multiply the number of cars remaining by the cost to produce each individual car. Then sum the results of the multiplication together.

[type conversions]: https://kotlinlang.org/docs/numbers.html#explicit-number-conversions
[modulo operator]: https://www.baeldung.com/kotlin/integer-division-modulus
Loading