roc/compiler/builtins/docs/Real.roc
Richard Feldman 045f1627a0 Try Real
2020-05-11 23:43:30 -04:00

293 lines
10 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

interface Real
exposes [
Real,
fromNum,
round,
ceiling,
floor,
div,
mod,
recip,
sqrt,
maxF32,
maxF64,
maxRatio,
minF32,
minF64,
minReal,
sin,
cos,
tan,
asin,
acos,
atan
]
imports []
## Types
## A [real number](https://en.wikipedia.org/wiki/Real_number). All number literals with decimal points are #Real values.
##
## ## Precision
##
## Every programming language is bound by finite hardware, which means numbers
## in every programming language have necessarily limited precision. (For example, imagine
## trying to work with the individual digits in a quintillion raised to itself
## a quintillion times over. If that sounds physically representable, you can repeat
## that operation until it isn't; eventually there aren't enough atoms in the universe
## to possibly represent all the digits. Math is bigger than the physical reality in which hardware exists!)
##
## For this reason, Roc has limited precision for storing reals.
##
## Rational reals like `-3.0` and `1/3` can be represented precisely in the #Ratio type, up
## to a certain size. Irrational reals, such as [π](https://en.wikipedia.org/wiki/Pi) or
## the square root of 2, are approximated.
##
## >>> 0.1
##
## >>> 1.0
##
## >>> 0.0
##
## If you like, you can put underscores in your #Real literals.
## They have no effect on the number's value, but can make things easier to read.
##
## >>> 1_000_000.000_000_001
##
## A #Real values can be either a *floating-point* values or a #Ratio value.
## Floating-point values (#F16, #F32, #F64, or #F128) commmonly have hardware
## support, making them run faster than #Ratio. However, #Ratio is more precise
## for arithmetic instructions.
##
## For example, compare the outputs of adding 0.1 and 0.2 in ratios compared to floats:
##
## >>> 0.1r + 0.2r
##
## >>> 0.1f64 + 0.2f64
##
## Floating point values work this way because of some (very reasonable)
## hardware design decisions made in 1985, which are hardwired into all modern processors.
## The performance penalty for having things work any other way than this is severe, so
## for performance-critical code, it is generally best to choose floats over ratios.
## In contrast, for precision-critical code (like anything dealing with money),
## it's generally best to choose ratios over floats.
##
## Like #Int, it's possible for #Real operations to overflow.
##
## Although some languages treat have first-class representations for
## `-Infinity`, `Infinity`, and the special `NaN` ("not a number")
## floating-point values described in the IEEE-754, Roc does not.
## Instead, Roc treats all of these as errors. If any floating-point operation
## in encounters one of these values, it will deal with the error in some other way.
##
## ## Loud versus Quiet errors
##
## Besides precision problems, another reason floats are error-prone
## is that they have quiet error handling built in. For example, in
## a 64-bit floating point number, there are certain patterns of those
## 64 bits which do not represent valid floats; instead, they represent
## erroneous results of previous operations.
##
## Whenever any arithmetic operation is performed on an erroneous float,
## the result is also erroneous. This is called *error propagation*, and
## it is notoriously error-prone. In Roc, using equality operations like
## `==` and `!=` on an erroneous float causes a crash. (See #Float.isErroneous
## for other ways to check what erroneous value you have.)
##
## Beause erroneous floats are so error-prone, Roc discourages using them.
## Instead, by default it treats them the same way as overflow: by
## crashing whenever any #Float function would otherwise return one.
## You can also use functions like #Float.tryAdd to get an `Ok` or an error
## back so you can gracefully recover from erroneous values.
##
## Quiet errors can be useful sometimes. For example, you might want to
## do three floating point calculations in a row, and then gracefully handle
## the situation where any one of the three was erroneous. In that situation,
## quiet errors can be more efficient than using three `try` functions, because
## it can have one condition at the end instead of three along the way.
##
## ## Performance Notes
##
## Currently, loud errors are implemented using an extra conditional. Although
## this conditional will always be correctly branh-predicted unless an error
## occurs, there is a small effect on the instruction cache, which means
## quiet errors are very slightly more efficient.
##
## Long-term, it's possible that the Roc compiler may be able to implement
## loud errors using *signalling errors* in some situations, which could
## eliminate the performance difference between loud and quiet errors in
## the situation where no error occurs.
## Conversions
toF32 : Num * -> F64
toF64 : Num * -> F64
toRatio : Num * -> Ratio
round : Real * -> Int
ceil : Real * -> Int
floor : Real * -> Int
## Trigonometry
## Return an approximation of a number's cosine.
##
## For #Ratio values, this will convert to #F64, perform the operation, and
## convert back to #Ratio. So calling `Real.cos ratio` is the same as calling
## `Real.toRatio (Real.cos (Real.toF64 ratio))`.
cos : Real a -> Real a
acos : Real a -> Real a
sin : Real a -> Real a
asin : Real a -> Real a
tan : Real a -> Real a
atan : Real a -> Real a
## Other Calculations (arithmetic?)
## Divide two #Real numbers.
##
## `a / b` is shorthand for `Real.div a b`.
##
## Division by zero is undefined in mathematics. As such, you should make
## sure never to pass zero as the denomaintor to this function!
##
## If zero does get passed as the denominator...
##
## * In a development build, you'll get an assertion failure.
## * In a release build, the function will return `Infinity`, `-Infinity`, or `NaN` depending on the arguments.
##
## To divide an #Int and a #Real, first convert the #Int to a #Real using one of the functions in this module.
##
## >>> 5.0 / 7.0
##
## >>> Real.div 5 7
##
## `Real.div` can be convenient in pipelines.
##
## >>> Real.piF64
## >>> |> Real.div 2.0
div : Real a, Real a -> Result Real DivByZero
## Perform modulo on two #Real numbers.
##
## Modulo is the same as remainder when working with positive numbers,
## but if either number is negative, then modulo works differently.
##
## Return `Err DivByZero` if the second number is zero, because division by zero is undefined in mathematics.
##
## `a % b` is shorthand for `Real.mod a b`.
##
## >>> 5.0 % 7.0
##
## >>> Real.mod 5 7
##
## `Real.mod` can be convenient in pipelines.
##
## >>> Real.piF64
## >>> |> Real.mod 2.0
mod : Real a, Real a -> Result Real DivByZero
tryMod : Real a, Real a -> Result (Real a) [ DivByZero ]*
## Return the reciprocal of a #Real - that is, divides `1.0` by the given number.
##
## Crashes if given `0.0`, because division by zero is undefined in mathematics.
##
## For a version that does not crash, use #tryRecip
recip : Real a -> Result (Real a) [ DivByZero ]*
tryRecip : Real a -> Result (Real a) [ DivByZero ]*
## Return an approximation of the absolute value of the square root of the #Real.
##
## Return #InvalidSqrt if given a negative number or an erroneous #Real. The square root of a negative number is an [imaginary number](https://en.wikipedia.org/wiki/Imaginary_number), which is not one of the real numbers.
##
## >>> Real.sqrt 4.0
##
## >>> Real.sqrt 1.5
##
## >>> Real.sqrt 0.0
##
## >>> Real.sqrt -4.0
sqrt : Real a -> [Ok (Real a), InvalidSqrt]*
## Like #Real.sqrt, but returning a *quiet NaN* if given a negative number.
##
## Quiet NaNs are notoriously more error-prone than explicit #Ok unions,
## so if you're using this instead of #Real.sqrt, be very careful not to let
## potential error cases go unhandled.
##
## ## Performance Notes
##
## This runs faster than #Real.sqrt, but is more error-prone because it makes
## it easier to forget to handle potential error cases. You may not forget
## when you just got done reading this paragraph, but the next person who
## comes along to modify the code may not have read it at all, and might not
## realize the need for seurity checks beause the requirement is implicit.
sqrtQuiet : Real a -> Real a
## Constants
## An approximation of [e](https://en.wikipedia.org/wiki/E_(mathematical_constant))
## in F64 form, specifically 2.718281828459045.
eF64 : F64
## An approximation of [pi](https://en.wikipedia.org/wiki/Pi)
## in F64 form, specifically 3.141592653589793.
piF64 : F64
## Sort ascending - that is, with the lowest first, and the highest last.
##
## List.sort Real.asc [ 3.0, 6.0, 0.0 ]
##
asc : Real a, Real a -> [ Eq, Lt, Gt ]
## Sort descending - that is, with the highest first, and the lowest last.
##
## List.sort Real.desc [ 3.0, 6.0, 0.0 ]
##
desc : Real a, Real a -> [ Eq, Lt, Gt ]
## Limits
## The highest supported #F64 value you can have, which is approximately 1.8 × 10^308.
##
## If you go higher than this, your running Roc code will crash - so be careful not to!
maxF64 : F64
## The lowest supported #F64 value you can have, which is approximately -1.8 × 10^308.
##
## If you go lower than this, your running Roc code will crash - so be careful not to!
minF64 : F64
## The highest integer that can be represented as a #F64 without # losing precision.
## It is equal to 2^53, which is approximately 9 × 10^15.
##
## Some integers higher than this can be represented, but they may lose precision. For example:
##
## >>> Real.highestInt
##
## >>> Real.highestInt + 100 # Increasing may lose precision
##
## >>> Real.highestInt - 100 # Decreasing is fine - but watch out for lowestLosslessInt!
maxIntF64 : Real *
## The lowest integer that can be represented as a #F64 without losing precision.
## It is equal to -2^53, which is approximately -9 × 10^15.
##
## Some integers lower than this can be represented, but they may lose precision. For example:
##
## >>> Real.lowestIntVal
##
## >>> Real.lowestIntVal - 100 # Decreasing may lose precision
##
## >>> Real.lowestIntVal + 100 # Increasing is fine - but watch out for highestInt!
minIntF64 : Real *