Num

Num range

Represents a number that could be either an Int or a Frac.

This is useful for functions that can work on either, for example Num.add, whose type is:

add : Num a, Num a -> Num a

The number 1.5 technically has the type Num (Fraction *), so when you pass two of them to Num.add, the answer you get is 3.0 : Num (Fraction *).

Similarly, the number 0x1 (that is, the integer 1 in hexadecimal notation) technically has the type Num (Integer *), so when you pass two of them to Num.add, the answer you get is 2 : Num (Integer *).

The type Frac a is defined to be an alias for Num (Fraction a), so 3.0 : Num (Fraction *) is the same value as 3.0 : Frac *. Similarly, the type Int a is defined to be an alias for Num (Integer a), so 2 : Num (Integer *) is the same value as 2 : Int *.

In this way, the Num type makes it possible to have 1 + 0x1 return 2 : Int * and 1.5 + 1.5 return 3.0 : Frac.

Number Literals

Number literals without decimal points (like 0, 4 or 360) have the type Num * at first, but usually end up taking on a more specific type based on how they're used.

For example, in (1 + List.len myList), the 1 has the type Num * at first, but because List.len returns a U64, the 1 ends up changing from Num * to the more specific U64, and the expression as a whole ends up having the type U64.

Sometimes number literals don't become more specific. For example, the Num.toStr function has the type Num * -> Str. This means that when calling Num.toStr (5 + 6), the expression (5 + 6) still has the type Num *. When this happens, Num * defaults to being an I64 - so this addition expression would overflow if either 5 or 6 were replaced with a number big enough to cause addition overflow on an I64 value.

If this default of I64 is not big enough for your purposes, you can add an i128 to the end of the number literal, like so:

Num.toStr 5_000_000_000i128

This i128 suffix specifies that you want this number literal to be an I128 instead of a Num *. All the other numeric types have suffixes just like i128; here are some other examples:

In practice, these are rarely needed. It's most common to write number literals without any suffix.

Int range

A fixed-size integer - that is, a number with no fractional component.

Integers come in two flavors: signed and unsigned. Signed integers can be negative ("signed" refers to how they can incorporate a minus sign), whereas unsigned integers cannot be negative.

Since integers have a fixed size, the size you choose determines both the range of numbers it can represent, and also how much memory it takes up.

U8 is an an example of an integer. It is an unsigned Int that takes up 8 bits (aka 1 byte) in memory. The U is for Unsigned and the 8 is for 8 bits. Because it has 8 bits to work with, it can store 256 numbers (2^8), and because it is unsigned, its min value is 0. This means the 256 numbers it can store range from 0 to 255.

I8 is a signed integer that takes up 8 bits. The I is for Integer, since integers in mathematics are signed by default. Because it has 8 bits just like U8, it can store 256 numbers (still 2^8), but because it is signed, the range is different. Its 256 numbers range from -128 to 127.

Here are some other examples:

This pattern continues up to U128 and I128.

Performance Details

In general, using smaller numeric sizes means your program will use less memory. However, if a mathematical operation results in an answer that is too big or too small to fit in the size available for that answer (which is typically the same size as the inputs), then you'll get an overflow error.

As such, minimizing memory usage without causing overflows involves choosing number sizes based on your knowledge of what numbers you expect your program to encounter at runtime.

Minimizing memory usage does not imply maximum runtime speed! CPUs are typically fastest at performing integer operations on integers that are the same size as that CPU's native machine word size. That means a 64-bit CPU is typically fastest at executing instructions on U64 and I64 values, whereas a 32-bit CPU is typically fastest on U32 and I32 values.

Putting these factors together, here are some reasonable guidelines for optimizing performance through integer size choice:

All number literals without decimal points are compatible with Int values.

You can optionally put underscores in your Int literals. They have no effect on the number's value, but can make large numbers easier to read.

1_000_000

Integers come in two flavors: signed and unsigned.

Integers also come in different sizes. Choosing a size depends on your performance needs and the range of numbers you need to represent. At a high level, the general trade-offs are:

Here are the different fixed size integer types:

RangeTypeSize
-128I81 Byte
127
-------------------------------------------------------------------------
0U81 Byte
255
-------------------------------------------------------------------------
-32_768I162 Bytes
32_767
-------------------------------------------------------------------------
0U162 Bytes
65_535
-------------------------------------------------------------------------
-2_147_483_648I324 Bytes
2_147_483_647
-------------------------------------------------------------------------
0U324 Bytes
(over 4 billion) 4_294_967_295
-------------------------------------------------------------------------
-9_223_372_036_854_775_808I648 Bytes
9_223_372_036_854_775_807
-------------------------------------------------------------------------
0U648 Bytes
(over 18 quintillion) 18_446_744_073_709_551_615
-------------------------------------------------------------------------
-170_141_183_460_469_231_731_687_303_715_884_105_728I12816 Bytes
170_141_183_460_469_231_731_687_303_715_884_105_727
-------------------------------------------------------------------------
(over 340 undecillion) 0U12816 Bytes
340_282_366_920_938_463_463_374_607_431_768_211_455

If any operation would result in an Int that is either too big or too small to fit in that range (e.g. calling Num.maxI32 + 1), then the operation will overflow. When an overflow occurs, the program will crash.

As such, it's very important to design your code not to exceed these bounds! If you need to do math outside these bounds, consider using a larger numeric size.

Frac range

A fixed-size number with a fractional component.

Roc fractions come in two flavors: fixed-point base-10 and floating-point base-2.

If you don't specify a type, Roc will default to using Dec because it's the least error-prone overall. For example, suppose you write this:

wasItPrecise = 0.1 + 0.2 == 0.3

The value of wasItPrecise here will be Bool.true, because Roc uses Dec by default when there are no types specified.

In contrast, suppose we use f32 or f64 for one of these numbers:

wasItPrecise = 0.1f64 + 0.2 == 0.3

Here, wasItPrecise will be Bool.false because the entire calculation will have been done in a base-2 floating point calculation, which causes noticeable precision loss in this case.

The floating-point numbers (F32 and F64) also have three values which are not ordinary finite numbers. They are:

These values are different from ordinary numbers in that they only occur when a floating-point calculation encounters an error. For example:

These rules come from the IEEE-754 floating point standard. Because almost all modern processors are built to this standard, deviating from these rules has a significant performance cost! Since the most common reason to choose F64 or F32 over Dec is access to hardware-accelerated performance, Roc follows these rules exactly.

There's no literal syntax for these error values, but you can check to see if you ended up with one of them by using #isNaN, #isFinite, and #isInfinite. Whenever a function in this module could return one of these values, that possibility is noted in the function's documentation.

Performance Details

On typical modern CPUs, performance is similar between Dec, F64, and F32 for addition and subtraction. For example, F32 and F64 do addition using a single CPU floating-point addition instruction, which typically takes a few clock cycles to complete. In contrast, Dec does addition using a few CPU integer arithmetic instructions, each of which typically takes only one clock cycle to complete. Exact numbers will vary by CPU, but they should be similar overall.

Dec is significantly slower for multiplication and division. It not only needs to do more arithmetic instructions than F32 and F64 do, but also those instructions typically take more clock cycles to complete.

With Num.sqrt and trigonometry functions like Num.cos, there is an even bigger performance difference. F32 and F64 can do these in a single instruction, whereas Dec needs entire custom procedures - which use loops and conditionals. If you need to do performance-critical trigonometry or square roots, either F64 or F32 is probably a better choice than the usual default choice of Dec, despite the precision problems they bring.

Signed128

Signed64

Signed32

Signed16

Signed8

Unsigned128

Unsigned64

Unsigned32

Unsigned16

Unsigned8

Integer range

I128

I64

I32

I16

I8

A signed 8-bit integer, ranging from -128 to 127

U128

U64

U32

U16

U8

Decimal

Binary64

Binary32

FloatingPoint range

F64

A 64-bit IEEE 754 binary floating-point number.

F64 represents decimal numbers less precisely than Dec does, but operations on it can be faster because CPUs have hardware-level support for F64 but not Dec. There are other tradeoffs between the two, such as:

F32

A 32-bit IEEE 754 binary floating-point number.

This works just like F64 (see its docs for a comparison with Dec) except it's smaller. That in turn means it takes up less memory, but can store smaller numbers (and becomes imprecise more easily than F64 does).

Dec

A decimal number.

Dec is a more precise way to represent decimal numbers (like currency) than F32 and F64 are, because Dec is represented in memory as base-10. In contrast, F64 and F32 are base-2 in memory, which can lead to decimal precision loss even when doing addition and subtraction. For example, when using F64, 0.1 + 0.2 returns 0.30000000000000004, whereas when using Dec, 0.1 + 0.2 returns 0.3.

Under the hood, a Dec is an I128, and operations on it perform base-10 fixed-point arithmetic with 18 decimal places of precision.

This means a Dec can represent whole numbers up to slightly over 170 quintillion, along with 18 decimal places. (To be precise, it can store numbers between -170_141_183_460_469_231_731.687303715884105728 and 170_141_183_460_469_231_731.687303715884105727.) Why 18 decimal places? It's the highest number of decimal places where you can still convert any U64 to a Dec without losing information.

There are some use cases where F64 and F32 can be better choices than Dec despite their issues with base-10 numbers. For example, in graphical applications they can be a better choice for representing coordinates because they take up less memory, certain relevant calculations run faster (see performance details, below), and base-10 generally isn't as big a concern when dealing with screen coordinates as it is when dealing with currency.

Another scenario where F64 can be a better choice than Dec is when representing extremely small numbers. The smallest positive F64 that can be represented without precision loss is 2^(−1074), which is about 5 * 10^(-324). Here is that number next to the smallest Dec that can be represented:

This is because floating-point numbers like F64 can gain more digits of precision after the . when they aren't using as many digits before the . - and this applies the most if the digit before . is 0. So whereas Dec always has 18 digits after the ., the number of digits after the . that F32 can F64 can represent without precision loss depends on what comes before it.

Performance Details

CPUs have dedicated instructions for many F32 and F64 operations, but none for Dec. Internally, Dec is represented as a 128-bit integer and uses multiple instructions to perform fractional operations. This gives F32 and F64 performance advantages for many operations.

Here's a comparison of about how long Dec takes to perform a given operation compared to F64, based on benchmarks on an M1 CPU:

Keep in mind that arithmetic instructions are basically the fastest thing a CPU does, so (for example) a network request that takes 10 milliseconds to complete would go on this list as about 10000000x. So these performance differences might be more or less noticeable than the base-10 representation differences depending on the use case.

e : Frac *

Euler's number (e)

pi : Frac *

Archimedes' constant (Ï€)

tau : Frac *

Circle constant (Ï„)

toStr : Num * -> Str

Convert a number to a Str.

Num.toStr 42

Only Frac values will include a decimal point, and they will always include one.

Num.toStr 4.2
Num.toStr 4.0

When this function is given a non-finite F64 or F32 value, the returned string will be "NaN", "∞", or "-∞".

intCast : Int a -> Int b

Convert an Int to a new Int of the expected type:

# Casts a U8 to a U16
x : U16
x = Num.intCast 255u8

In the case of downsizing, information is lost:

# returns 0, as the bits were truncated.
x : U8
x = Num.intCast 256u16

compare : Num a, Num a -> [ LT, EQ, GT ]

isLt : Num a, Num a -> Bool

Returns Bool.true if the first number is less than the second.

a < b is shorthand for Num.isLt a b.

If either argument is NaN, returns Bool.false no matter what. (NaN is defined to be unordered.)

5
    |> Num.isLt 6

isGt : Num a, Num a -> Bool

Returns Bool.true if the first number is greater than the second.

a > b is shorthand for Num.isGt a b.

If either argument is NaN, returns Bool.false no matter what. (NaN is defined to be unordered.)

6
    |> Num.isGt 5

isLte : Num a, Num a -> Bool

Returns Bool.true if the first number is less than or equal to the second.

a <= b is shorthand for Num.isLte a b.

If either argument is NaN, returns Bool.false no matter what. (NaN is defined to be unordered.)

isGte : Num a, Num a -> Bool

Returns Bool.true if the first number is greater than or equal to the second.

a >= b is shorthand for Num.isGte a b.

If either argument is NaN, returns Bool.false no matter what. (NaN is defined to be unordered.)

isApproxEq : Frac a, Frac a, { rtol ? Frac a, atol ? Frac a } -> Bool

Returns Bool.true if the first number and second number are within a specific threshold

A specific relative and absolute tolerance can be provided to change the threshold

This function is symmetric: Num.isApproxEq a b == Num.isApproxEq b a

If either argument is NaN, returns Bool.false no matter what. (NaN is defined to be unordered.)

isZero : Num a -> Bool

Returns Bool.true if the number is 0, and Bool.false otherwise.

isEven : Int a -> Bool

A number is even if dividing it by 2 gives a remainder of 0.

Examples of even numbers: 0, 2, 4, 6, 8, -2, -4, -6, -8

isOdd : Int a -> Bool

A number is odd if dividing it by 2 gives a remainder of 1.

Examples of odd numbers: 1, 3, 5, 7, -1, -3, -5, -7

isPositive : Num a -> Bool

Positive numbers are greater than 0.

isNegative : Num a -> Bool

Negative numbers are less than 0.

toFrac : Num * -> Frac *

isNaN : Frac * -> Bool

Returns Bool.true if the Frac is not a number as defined by IEEE-754

Num.isNaN (0 / 0)

isInfinite : Frac * -> Bool

Returns Bool.true if the Frac is positive or negative infinity as defined by IEEE-754

Num.isInfinite (1 / 0)

Num.isInfinite (-1 / 0)

isFinite : Frac * -> Bool

Returns Bool.true if the Frac is not an infinity as defined by IEEE-754

Num.isFinite 42

abs : Num a -> Num a

Returns the absolute value of the number.

Num.abs 4

Num.abs -2.5

Num.abs 0

Num.abs 0.0

This is safe to use with any Frac, but it can cause overflow when used with certain Int values.

For example, calling #Num.abs on the lowest value of a signed integer (such as Num.minI64 or Num.minI32) will cause overflow. This is because, for any given size of signed integer (32-bit, 64-bit, etc.) its negated lowest value turns out to be 1 higher than the highest value it can represent. (For this reason, calling Num.neg on the lowest signed value will also cause overflow.)

Calling this on an unsigned integer (like U32 or U64) never does anything.

absDiff : Num a, Num a -> Num a

Returns the absolute difference between two numbers.

Num.absDiff 5 3

Num.absDiff -3 5

Num.absDiff 3.0 5.0

If the answer to this operation can't fit in the return value (e.g. an I8 answer that's higher than 127 or lower than -128), the result is an overflow. For F64 and F32, overflow results in an answer of either ∞ or -∞. For all other number types, overflow results in a panic.

neg : Num a -> Num a

Returns a negative number when given a positive one, and vice versa.

Num.neg 5

Num.neg -2.5

Num.neg 0

Num.neg 0.0

!! Num.neg is not completely implemented for all types in all contexts, see github.com/roc-lang/roc/issues/6959 You can use \someNum -> 0 - someNum as a workaround.

This is safe to use with any Frac, but it can cause overflow when used with certain Int values.

For example, calling #Num.neg on the lowest value of a signed integer (such as Num.minI64 or Num.minI32) will cause overflow. This is because, for any given size of signed integer (32-bit, 64-bit, etc.) its negated lowest value turns out to be 1 higher than the highest value it can represent. (For this reason, calling #Num.abs on the lowest signed value will also cause overflow.)

Additionally, calling #Num.neg on any unsigned integer (such as any U64 or U32 value) other than zero will cause overflow.

(It will never crash when given a Frac, however, because of how floating point numbers represent positive and negative numbers.)

add : Num a, Num a -> Num a

Adds two numbers of the same type.

(To add an Int and a Frac, first convert one so that they both have the same type. There are functions in this module that can convert both Int to Frac and the other way around.)

a + b is shorthand for Num.add a b.

5 + 7

Num.add 5 7

Num.add can be convenient in pipelines.

Frac.pi
    |> Num.add 1.0

If the answer to this operation can't fit in the return value (e.g. an I8 answer that's higher than 127 or lower than -128), the result is an overflow. For F64 and F32, overflow results in an answer of either ∞ or -∞. For all other number types, overflow results in a panic.

sub : Num a, Num a -> Num a

Subtracts two numbers of the same type.

(To subtract an Int and a Frac, first convert one so that they both have the same type. There are functions in this module that can convert both Int to Frac and the other way around.)

a - b is shorthand for Num.sub a b.

7 - 5

Num.sub 7 5

Num.sub can be convenient in pipelines.

Frac.pi
    |> Num.sub 2.0

If the answer to this operation can't fit in the return value (e.g. an I8 answer that's higher than 127 or lower than -128), the result is an overflow. For F64 and F32, overflow results in an answer of either ∞ or -∞. For all other number types, overflow results in a panic.

mul : Num a, Num a -> Num a

Multiplies two numbers of the same type.

(To multiply an Int and a Frac, first convert one so that they both have the same type. There are functions in this module that can convert both Int to Frac and the other way around.)

a * b is shorthand for Num.mul a b.

5 * 7

Num.mul 5 7

Num.mul can be convenient in pipelines.

Frac.pi
    |> Num.mul 2.0

If the answer to this operation can't fit in the return value (e.g. an I8 answer that's higher than 127 or lower than -128), the result is an overflow. For F64 and F32, overflow results in an answer of either ∞ or -∞. For all other number types, overflow results in a panic.

min : Num a, Num a -> Num a

Obtains the smaller between two numbers of the same type.

Num.min 100 0

Num.min 3.0 -3.0

max : Num a, Num a -> Num a

Obtains the greater between two numbers of the same type.

Num.max 100 0

Num.max 3.0 -3.0

sin : Frac a -> Frac a

cos : Frac a -> Frac a

tan : Frac a -> Frac a

asin : Frac a -> Frac a

acos : Frac a -> Frac a

atan : Frac a -> Frac a

sqrt : Frac a -> Frac a

Returns an approximation of the absolute value of a Frac's square root.

The square root of a negative number is an irrational number, and Frac only supports rational numbers. As such, you should make sure never to pass this function a negative number! Calling sqrt on a negative Dec will cause a panic.

Calling sqrt on F32 and F64 values follows these rules:

These rules come from the IEEE-754 floating point standard. Because almost all modern processors are built to this standard, deviating from these rules has a significant performance cost! Since the most common reason to choose F64 or F32 over Dec is access to hardware-accelerated performance, Roc follows these rules exactly.

Num.sqrt 4.0

Num.sqrt 1.5

Num.sqrt 0.0

Num.sqrt -4.0f64

sqrtChecked : Frac a -> Result (Frac a) [SqrtOfNegative]

log : Frac a -> Frac a

Natural logarithm

logChecked : Frac a -> Result (Frac a) [LogNeedsPositive]

div : Frac a, Frac a -> Frac a

Divides one Frac by another.

a / b is shorthand for Num.div a b.

Division by zero is undefined in mathematics. As such, you should make sure never to pass zero as the denominator to this function! Calling div on a Dec denominator of zero will cause a panic.

Calling div on F32 and F64 values follows these rules:

These rules come from the IEEE-754 floating point standard. Because almost all modern processors are built to this standard, deviating from these rules has a significant performance cost! Since the most common reason to choose F64 or F32 over Dec is access to hardware-accelerated performance, Roc follows these rules exactly.

To divide an Int and a Frac, first convert the Int to a Frac using one of the functions in this module like #toDec.

5.0 / 7.0

Num.div 5 7

Num.div can be convenient in pipelines.

Num.pi
    |> Num.div 2.0

divChecked : Frac a, Frac a -> Result (Frac a) [DivByZero]

divCeil : Int a, Int a -> Int a

divCeilChecked : Int a, Int a -> Result (Int a) [DivByZero]

divTrunc : Int a, Int a -> Int a

Divides two integers, truncating the result towards zero.

a // b is shorthand for Num.divTrunc a b.

Division by zero is undefined in mathematics. As such, you should make sure never to pass zero as the denominator to this function! If you do, it will crash.

5 // 7

Num.divTrunc 5 7

8 // -3

Num.divTrunc 8 -3

divTruncChecked : Int a, Int a -> Result (Int a) [DivByZero]

rem : Int a, Int a -> Int a

Obtains the remainder (truncating modulo) from the division of two integers.

a % b is shorthand for Num.rem a b.

5 % 7

Num.rem 5 7

-8 % -3

Num.rem -8 -3

remChecked : Int a, Int a -> Result (Int a) [DivByZero]

isMultipleOf : Int a, Int a -> Bool

bitwiseAnd : Int a, Int a -> Int a

Does a "bitwise and". Each bit of the output is 1 if the corresponding bit of x AND of y is 1, otherwise it's 0.

bitwiseXor : Int a, Int a -> Int a

Does a "bitwise exclusive or". Each bit of the output is the same as the corresponding bit in x if that bit in y is 0, and it's the complement of the bit in x if that bit in y is 1.

bitwiseOr : Int a, Int a -> Int a

Does a "bitwise or". Each bit of the output is 0 if the corresponding bit of x OR of y is 0, otherwise it's 1.

bitwiseNot : Int a -> Int a

Returns the complement of x - the number you get by switching each 1 for a 0 and each 0 for a 1. This is the same as -x - 1.

shiftLeftBy : Int a, U8 -> Int a

Bitwise left shift of a number by another

The least significant bits always become 0. This means that shifting left is like multiplying by factors of two for unsigned integers.

shiftLeftBy 0b0000_0011 2 == 0b0000_1100

0b0000_0101 |> shiftLeftBy 2 == 0b0001_0100

In some languages shiftLeftBy is implemented as a binary operator <<.

shiftRightBy : Int a, U8 -> Int a

Bitwise arithmetic shift of a number by another

The most significant bits are copied from the current.

shiftRightBy 0b0000_1100 2 == 0b0000_0011

0b0001_0100 |> shiftRightBy 2 == 0b0000_0101

0b1001_0000 |> shiftRightBy 2 == 0b1110_0100

In some languages shiftRightBy is implemented as a binary operator >>>.

shiftRightZfBy : Int a, U8 -> Int a

Bitwise logical right shift of a number by another

The most significant bits always become 0. This means that shifting right is like dividing by factors of two for unsigned integers.

shiftRightZfBy 0b0010_1000 2 == 0b0000_1010

0b0010_1000 |> shiftRightZfBy 2 == 0b0000_1010

0b1001_0000 |> shiftRightZfBy 2 == 0b0010_0100

In some languages shiftRightZfBy is implemented as a binary operator >>.

round : Frac * -> Int *

Round off the given fraction to the nearest integer.

floor : Frac * -> Int *

ceiling : Frac * -> Int *

pow : Frac a, Frac a -> Frac a

Raises a Frac to the power of another Frac.

For an Int alternative to this function, see Num.powInt

powInt : Int a, Int a -> Int a

Raises an integer to the power of another, by multiplying the integer by itself the given number of times.

This process is known as exponentiation by squaring.

For a Frac alternative to this function, which supports negative exponents, see #Num.pow.

Warning

It is very easy for this function to produce an answer so large it causes an overflow.

countLeadingZeroBits : Int a -> U8

Counts the number of most-significant (leading in a big-Endian sense) zeroes in an integer.

Num.countLeadingZeroBits 0b0001_1100u8

3

Num.countLeadingZeroBits 0b0000_0000u8

8

countTrailingZeroBits : Int a -> U8

Counts the number of least-significant (trailing in a big-Endian sense) zeroes in an integer.

Num.countTrailingZeroBits 0b0001_1100u8

2

Num.countTrailingZeroBits 0b0000_0000u8

8

countOneBits : Int a -> U8

Counts the number of set bits in an integer.

Num.countOneBits 0b0001_1100u8

3

Num.countOneBits 0b0000_0000u8

0

addWrap : Int range, Int range -> Int range

addSaturated : Num a, Num a -> Num a

Adds two numbers, clamping on the maximum representable number rather than overflowing.

This is the same as Num.add except for the saturating behavior if the addition is to overflow. For example, if x : U8 is 200 and y : U8 is 100, addSaturated x y will yield 255, the maximum value of a U8.

addChecked : Num a, Num a -> Result (Num a) [Overflow]

Adds two numbers and checks for overflow.

This is the same as Num.add except if the operation overflows, instead of panicking or returning ∞ or -∞, it will return Err Overflow.

subWrap : Int range, Int range -> Int range

subSaturated : Num a, Num a -> Num a

Subtracts two numbers, clamping on the minimum representable number rather than overflowing.

This is the same as Num.sub except for the saturating behavior if the subtraction is to overflow. For example, if x : U8 is 10 and y : U8 is 20, subSaturated x y will yield 0, the minimum value of a U8.

subChecked : Num a, Num a -> Result (Num a) [Overflow]

Subtracts two numbers and checks for overflow.

This is the same as Num.sub except if the operation overflows, instead of panicking or returning ∞ or -∞, it will return Err Overflow.

mulWrap : Int range, Int range -> Int range

mulSaturated : Num a, Num a -> Num a

Multiplies two numbers, clamping on the maximum representable number rather than overflowing.

This is the same as Num.mul except for the saturating behavior if the addition is to overflow.

mulChecked : Num a, Num a -> Result (Num a) [Overflow]

Multiplies two numbers and checks for overflow.

This is the same as Num.mul except if the operation overflows, instead of panicking or returning ∞ or -∞, it will return Err Overflow.

minI8 : I8

Returns the lowest number that can be stored in an I8 without underflowing its available memory and crashing.

For reference, this number is -128.

Note that the positive version of this number is larger than Num.maxI8, which means if you call Num.abs on Num.minI8, it will overflow and crash!

maxI8 : I8

Returns the highest number that can be stored in an I8 without overflowing its available memory and crashing.

For reference, this number is 127.

Note that this is smaller than the positive version of Num.minI8, which means if you call Num.abs on Num.minI8, it will overflow and crash!

minU8 : U8

Returns the lowest number that can be stored in a U8 without underflowing its available memory and crashing.

For reference, this number is zero, because U8 is unsigned, and zero is the lowest unsigned number. Unsigned numbers cannot be negative.

maxU8 : U8

Returns the highest number that can be stored in a U8 without overflowing its available memory and crashing.

For reference, this number is 255.

minI16 : I16

Returns the lowest number that can be stored in an I16 without underflowing its available memory and crashing.

For reference, this number is -32_768.

Note that the positive version of this number is larger than Num.maxI16, which means if you call Num.abs on Num.minI16, it will overflow and crash!

maxI16 : I16

Returns the highest number that can be stored in an I16 without overflowing its available memory and crashing.

For reference, this number is 32_767.

Note that this is smaller than the positive version of Num.minI16, which means if you call Num.abs on Num.minI16, it will overflow and crash!

minU16 : U16

Returns the lowest number that can be stored in a U16 without underflowing its available memory and crashing.

For reference, this number is zero, because U16 is unsigned, and zero is the lowest unsigned number. Unsigned numbers cannot be negative.

maxU16 : U16

Returns the highest number that can be stored in a U16 without overflowing its available memory and crashing.

For reference, this number is 65_535.

minI32 : I32

Returns the lowest number that can be stored in an I32 without underflowing its available memory and crashing.

For reference, this number is -2_147_483_648.

Note that the positive version of this number is larger than Num.maxI32, which means if you call Num.abs on Num.minI32, it will overflow and crash!

maxI32 : I32

Returns the highest number that can be stored in an I32 without overflowing its available memory and crashing.

For reference, this number is 2_147_483_647, which is over 2 million.

Note that this is smaller than the positive version of Num.minI32, which means if you call Num.abs on Num.minI32, it will overflow and crash!

minU32 : U32

Returns the lowest number that can be stored in a U32 without underflowing its available memory and crashing.

For reference, this number is zero, because U32 is unsigned, and zero is the lowest unsigned number. Unsigned numbers cannot be negative.

maxU32 : U32

Returns the highest number that can be stored in a U32 without overflowing its available memory and crashing.

For reference, this number is 4_294_967_295.

minI64 : I64

Returns the lowest number that can be stored in an I64 without underflowing its available memory and crashing.

For reference, this number is -9_223_372_036_854_775_808, which is under 9 quintillion.

Note that the positive version of this number is larger than Num.maxI64, which means if you call Num.abs on Num.minI64, it will overflow and crash!

maxI64 : I64

Returns the highest number that can be stored in an I64 without overflowing its available memory and crashing.

For reference, this number is 9_223_372_036_854_775_807, which is over 9 quintillion.

Note that this is smaller than the positive version of Num.minI64, which means if you call Num.abs on Num.minI64, it will overflow and crash!

minU64 : U64

Returns the lowest number that can be stored in a U64 without underflowing its available memory and crashing.

For reference, this number is zero, because U64 is unsigned, and zero is the lowest unsigned number. Unsigned numbers cannot be negative.

maxU64 : U64

Returns the highest number that can be stored in a U64 without overflowing its available memory and crashing.

For reference, this number is 18_446_744_073_709_551_615, which is over 18 quintillion.

minI128 : I128

Returns the lowest number that can be stored in an I128 without underflowing its available memory and crashing.

For reference, this number is -170_141_183_460_469_231_731_687_303_715_884_105_728. which is under 170 undecillion.

Note that the positive version of this number is larger than Num.maxI128, which means if you call Num.abs on Num.minI128, it will overflow and crash!

maxI128 : I128

Returns the highest number that can be stored in an I128 without overflowing its available memory and crashing.

For reference, this number is 170_141_183_460_469_231_731_687_303_715_884_105_727, which is over 170 undecillion.

Note that this is smaller than the positive version of Num.minI128, which means if you call Num.abs on Num.minI128, it will overflow and crash!

minU128 : U128

Returns the lowest number that can be stored in a U128 without underflowing its available memory and crashing.

For reference, this number is zero, because U128 is unsigned, and zero is the lowest unsigned number. Unsigned numbers cannot be negative.

maxU128 : U128

Returns the highest number that can be stored in a U128 without overflowing its available memory and crashing.

For reference, this number is 340_282_366_920_938_463_463_374_607_431_768_211_455, which is over 340 undecillion.

minF32 : F32

maxF32 : F32

minF64 : F64

maxF64 : F64

toI8 : Int * -> I8

Converts an Int to an I8. If the given number can't be precisely represented in an I8, the returned number may be different from the given number.

toI16 : Int * -> I16

toI32 : Int * -> I32

toI64 : Int * -> I64

toI128 : Int * -> I128

toU8 : Int * -> U8

toU16 : Int * -> U16

toU32 : Int * -> U32

toU64 : Int * -> U64

toU128 : Int * -> U128

toF32 : Num * -> F32

Converts a Num to an F32. If the given number can't be precisely represented in an F32, the returned number may be different from the given number.

toF64 : Num * -> F64

Converts a Num to an F64. If the given number can't be precisely represented in an F64, the returned number may be different from the given number.

toI8Checked : Int * -> Result I8 [OutOfBounds]

Converts a Int to an I8. If the given integer can't be precisely represented in an I8, returns Err OutOfBounds.

toI16Checked : Int * -> Result I16 [OutOfBounds]

toI32Checked : Int * -> Result I32 [OutOfBounds]

toI64Checked : Int * -> Result I64 [OutOfBounds]

toI128Checked : Int * -> Result I128 [OutOfBounds]

toU8Checked : Int * -> Result U8 [OutOfBounds]

toU16Checked : Int * -> Result U16 [OutOfBounds]

toU32Checked : Int * -> Result U32 [OutOfBounds]

toU64Checked : Int * -> Result U64 [OutOfBounds]

toU128Checked : Int * -> Result U128 [OutOfBounds]

toF32Checked : Num * -> Result F32 [OutOfBounds]

toF64Checked : Num * -> Result F64 [OutOfBounds]

withoutDecimalPoint : Dec -> I128

Turns a Dec into its I128 representation by removing the decimal point. This is equivalent to multiplying the Dec by 10^18.

withDecimalPoint : I128 -> Dec

Turns a I128 into the coresponding Dec by adding the decimal point. This is equivalent to dividing the I128 by 10^18.

f32ToParts : F32 -> { sign : Bool, exponent : U8, fraction : U32 }

Splits a F32 into its components according to IEEE 754 standard.

f64ToParts : F64 -> { sign : Bool, exponent : U16, fraction : U64 }

Splits a F64 into its components according to IEEE 754 standard.

f32FromParts : { sign : Bool, exponent : U8, fraction : U32 } -> F32

Combine parts of a F32 according to IEEE 754 standard. The fraction should not be bigger than 0x007F_FFFF, any bigger value will be truncated.

f64FromParts : { sign : Bool, exponent : U16, fraction : U64 } -> F64

Combine parts of a F64 according to IEEE 754 standard. The fraction should not be bigger than 0x000F_FFFF_FFFF_FFFF, any bigger value will be truncated. The exponent should not be bigger than 0x07FF, any bigger value will be truncated.

nanF32 : F32

The value for not-a-number for a F32 according to the IEEE 754 standard.

nanF64 : F64

The value for not-a-number for a F64 according to the IEEE 754 standard.

infinityF32 : F32

The value for infinity for a F32 according to the IEEE 754 standard.

infinityF64 : F64

The value for infinity for a F64 according to the IEEE 754 standard.