Floating-point numbers
On a Float every operator is IEEE-754 and `compare` is a total order that disagrees with them on `nan` and `-0.0`, and neither Float type is Hash.
Float32 and Float64 follow IEEE-754, which means == on them is not an equivalence relation: nan != nan. The
language gives a Float three ways to compare and keeps them deliberately different, rather than picking one and
hiding the other two.
Example#
const ratio: Float = 1.5
print "{ratio.floor()} {ratio.ceiling()} {ratio.round()} {ratio.squareRoot()}"
print "{0.0 == -0.0} {Float64.nan == Float64.nan} {Float64.nan.isNaN()}"
print "{Float64.infinity.isInfinite()} {ratio.isFinite()}"
print ratio.isCloseTo(1.5000001)Syntax#
Float32 Float64 the two widths; `Float` is an alias for `Float64`
value.isCloseTo(other, tolerance: 0.000001) comparison with a tolerance
Float64.pi Float64.e two of the named constants
Float64.nan Float64.infinity Float64.negativeInfinity the other three
value.isNaN() value.isInfinite() value.isFinite() what the three constants are compared againstRules#
Floatis an alias forFloat64. A decimal literal without an expected type isFloat64; see Literals.Every operator on a
Floatis exactly IEEE-754. That is==,!=,<,<=,>and>=:nan != nanis true, every comparison with ananon either side isfalse, and0.0 == -0.0is true.print(Float64.nan == Float64.nan) print(Float64.nan < 1.5) print(Float64.nan > 1.5) print(0.0 == -0.0)compareis a total order, and it disagrees with every operator on both of their exceptions.compareputsnanabove every other value and treats-0.0as equal to0.0, so a sort over floats terminates whatever pivot it picks and whatever is in the list - it never needs ananspecial case at the call site.A
Floatis the one type where an operator and the member behind it disagree, and that is why everything in the standard library that orders values -sort,sorted,minBy,maxBy- callscompareinstead of writing<=. Only a total order is an order at all; an IEEE<=would leave ananwherever it happened to start.const mixed = [1.5, Float64.nan, 0.5] print mixed.sorted({ _ }).toList()Float32andFloat64are deliberately notHash. A float can never be aMapkey, and there is nonankey to worry about, because the type that would need one does not have the trait a key needs.Does not compilefn needsHash<Value: Hash>(value: Value): Value { value } const ratio: Float = 1.5 print needsHash(ratio) // error: `Float64` does not implement `Hash`Where a tolerance is meant,
isCloseTosays so in the name.a.isCloseTo(b, tolerance: 0.000001)is(a - b).absolute() <= tolerance, with0.000001as the default tolerance.squareRoot,floor,ceiling,round,isNaN,isInfiniteandisFiniteare methods, not operators or free functions.pi,e,nan,infinityandnegativeInfinityare named constants ofFloat64(and, all butpiande, ofFloat32too).Float64.nanandFloat32.nanare the one way to writenanas a constant. Every other constant expression that producesnanis a compile error where it is written (Top-level code, rule 6) -nanis not computed from one of these two, it is the language's own name for it.print Float64.nanconst broken: Float64 = 0.0 / 0.0Showspells the three values that have no digits as words:nan,infand-inf.nanis written in lower case, the way the runtime and C write it and the way the constant is named (Float64.nan) - a name like any other, not the abbreviationNaN. Both back ends print the same three words.print "{Float64.nan} {Float64.infinity} {Float64.negativeInfinity}"
What this is not#
Float32/Float64 not being Hash is enforced wherever a bound is written on a type, not only on a function. A
bound on a function's type parameter is checked, as rule 4 shows, and the same bound written on a type's own
parameter (HashMap<Key: Hash, Value>) is checked against the type argument it is given too, so this is rejected:
use HashMap from "std/collections"
const table: HashMap<Float, String> = HashMap()
print table.length()
// error: `Float64` does not implement `Hash`Comparing for closeness is not the same question as comparing for order. isCloseTo answers "close enough for
this purpose", which needs a tolerance chosen by the caller; compare answers "which is greater", which needs an
answer for every pair including nan. Neither replaces the other.
Related#
- Integers - the fixed-width types a
Floatnever implicitly converts with. - Built-in types -
Floatin the context of every other built-in type. - Declaring a type - how
Equals,HashandShoware generated for a type.