mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 13:59:08 +00:00
293 lines
10 KiB
Text
293 lines
10 KiB
Text
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 *
|