std/linear/fixed
std/linear/src/fixed.trb
Fixed, the deterministic scalar: a whole number of 1/65536 parts, with every operation - the square root and the
trigonometry included - computed in integer arithmetic alone.
It exists for the one thing a Float cannot promise: the same bits everywhere. sine on a Float64 is whatever the
platform's mathematics library computes, and that differs between machines, between C libraries and between back
ends. A lockstep simulation, a replay and a checksum over a world state all need two machines to agree exactly, so
they run on a Fixed instead, and everything in std/linear and std/geometry that is generic over a Real scalar
works unchanged with one.
type Fixed
type Fixed with Signed, Real, Power, Hash, Show, TryFrom<String, NumberParseError>
A number held as a whole number of 1/65536 parts: the Q16.16 fixed-point scalar, and the only Real whose answers
are the same bits on every platform and out of every back end.
The field is the raw count, so Fixed(65536) is one and Fixed(1) is the smallest step there is. Build a value
with Fixed.from, Fixed.approximating or Fixed.tryFrom rather than by counting parts.
Fixed carries Real, so Vector2<Fixed>, Rectangle<Fixed> and every intersection test of std/geometry accept
it in place of Float with no other change to the code.
Examples
const half = Fixed.tryFrom("0.5").expect("half parses")
const three = Fixed.from 3
print "{three * half} {three.squareRoot()}"
Pitfalls
- The range of a product is the real limit, not the range of the type.
multiplyformsparts * other.partsbefore it scales back down, so two values above about 46341 overflow and the multiplication panics, although both operands are far insideFixed.largest. squareRootscales up before it takes the root, so it panics above about 2147483648 for the same reason.- Division and the remainder truncate towards zero, exactly as they do on
Int. Fixed.smallestcannot be shown or negated. Its count of parts is the smallestIntthere is, and that one has no positive counterpart - soabsoluteon it overflows, here as everywhere.- Every step is a multiple of
1/65536, so a value that a decimal writes exactly is usually not one aFixedholds exactly:Fixed.tryFrom("0.1")is0.100006103515625, andFixed.showwrites that, because that is the number.
Related
Real- the trait that makes this andFloat64interchangeable.Fixed.parts- the raw count, for a checksum or a wire format.
field parts
parts: Int64
The value counted in 1/65536 parts. Fixed(65536) is one, and Fixed(1) is the smallest step.
const partsPerWhole
static partsPerWhole: Int64 = 65536
How many parts one whole is: 65536, which is what makes this Q16.16.
const largest
static largest: Fixed = Fixed 9223372036854775807
The largest value the type holds. Arithmetic on it overflows long before this.
const smallest
static smallest: Fixed = Fixed(-9223372036854775807 - 1)
The smallest value the type holds.
const step
static step: Fixed = Fixed 1
The smallest step between two values: 1/65536.
const zero
static zero: Fixed = Fixed 0
The value nothing: no parts at all.
const one
static one: Fixed = Fixed 65536
The whole, which is 65536 parts.
const pi
static pi: Fixed = Fixed 205887
The ratio of a circle's circumference to its diameter, to the nearest part.
const tau
static tau: Fixed = Fixed 411775
A full turn in radians, to the nearest part.
const epsilon
static epsilon: Fixed = Fixed 1
One part, the same value as Fixed.step: the Real.epsilon of a fixed-point number.
const e
static e: Fixed = Fixed 178145
The base of the natural logarithm, to the nearest part: the Real.e of a fixed-point number.
fn approximating
static fn approximating(value: Float64): Fixed
The value a Float64 is closest to, rounded to the nearest part.
This is the bridge out of floating point, and it is deliberately not a From: it loses information, and a
conversion that loses information says so at the call.
print Fixed.approximating 0.5
Panics
When the value is not a number, or when its magnitude is above about 140737488355327 - the overflow of the
multiplication by 65536, which is an overflow like any other.
fn toFloat
fn toFloat(): Float64
The value as the nearest Float64. Exact for every Fixed whose magnitude is below 2^37.
fn toInt
fn toInt(): Int64
The whole part, truncated towards zero: what Int this value is, ignoring everything after the point.
fn add
fn add(other: Fixed): Fixed
The sum: two counts of parts added, exactly.
fn subtract
fn subtract(other: Fixed): Fixed
The difference: two counts of parts subtracted, exactly.
fn multiply
fn multiply(other: Fixed): Fixed
The product, scaled back down. Panics where the intermediate parts * other.parts leaves the range of an Int.
fn divide
fn divide(other: Fixed): Fixed
The quotient, truncated towards zero. Panics on a division by zero and where parts * 65536 overflows.
fn remainder
fn remainder(other: Fixed): Fixed
What is left after dividing out as many of the other as fit, with the sign of this value.
fn negate
fn negate(): Fixed
The value with its sign flipped.
fn absolute
fn absolute(): Fixed
The value without its sign.
fn equals
fn equals(other: Fixed): Bool
Whether the two hold the same count of parts, which is exact equality.
fn compare
fn compare(other: Fixed): Ordering
Which of the two is the smaller, as a total order with no value like a nan in it.
fn hash
fn hash(): Int
The count of parts itself: two values that are equal hold the same count.
fn show
fn show(): String
The exact decimal the value is, with at least one digit after the point. A Fixed is a multiple of 1/65536, so
the expansion always ends, after at most sixteen digits.
print Fixed.tryFrom("0.1").expect("a tenth parses")
fn tryFrom
static fn tryFrom(text: String): Result<Fixed, NumberParseError>
Reads a decimal, rounding to the nearest part. At most nine digits after the point are read; what follows them is below the resolution of the type anyway.
print Fixed.tryFrom("-2.25")
Errors
NumberParseErrorwhere the text is not a decimal, or where its whole part does not fit.
fn squareRoot
fn squareRoot(): Fixed
The non-negative square root, to the nearest part below the true one. Negative input answers zero.
fn sine
fn sine(): Fixed
The sine of this many radians, within about two parts of the true value.
fn cosine
fn cosine(): Fixed
The cosine of this many radians, within about two parts of the true value.
fn tangent
fn tangent(): Fixed
The sine over the cosine.
Panics
At a quarter turn and at three quarters, where the cosine is zero - a division by zero like any other.
fn arcSine
fn arcSine(): Fixed
The angle whose sine this is.
Panics
Outside [-1, 1], where there is no such angle - as Float64.arcSine does, so the two implementors of Real agree
(docs/design/LINEAR.md, "Numeric policy").
fn arcCosine
fn arcCosine(): Fixed
The angle whose cosine this is.
Panics
Outside [-1, 1], where there is no such angle, through Fixed.arcSine.
fn arcTangent
fn arcTangent(): Fixed
The angle in radians whose tangent this is.
fn arcTangentDivided
fn arcTangentDivided(by: Fixed): Fixed
The angle in radians of the point (by, self), in (-pi, pi].
fn exponential
fn exponential(): Fixed
e raised to this value, to within a part or two of the true value.
Panics
Above about 33.5, where the result no longer fits a Fixed. Far below zero the answer is zero, because that is
the nearest part.
fn naturalLogarithm
fn naturalLogarithm(): Fixed
The logarithm to the base e, to within a part or two of the true value.
Panics
At zero and below, where there is no logarithm. Float64.naturalLogarithm answers an infinity or nan there,
because that is what the platform's mathematics library answers.
fn power
fn power(exponent: Fixed): Fixed
This value raised to a Fixed power: e ** (exponent * ln self), computed with fourteen more bits than a Fixed
holds so that the logarithm does not lose them before the exponential needs them.
Panics
- Where the base is negative and the exponent is not whole: there is no real power then. A whole exponent of a
negative base is
Power<Int64>'s answer with its sign. - Where the base is zero and the exponent negative, which is a division by zero.
- Where the result does not fit a
Fixed.
fn floor
fn floor(): Fixed
The value rounded towards negative infinity.
fn ceiling
fn ceiling(): Fixed
The value rounded towards positive infinity.
fn round
fn round(): Fixed
The value rounded to the nearest whole number, halves away from zero.
fn halved
fn halved(): Fixed
Half the value, rounded towards zero by the last part.
fn radiansOfDegrees
fn radiansOfDegrees(): Fixed
The angle in radians that this many degrees is, rounded to the nearest part.
fn degreesOfRadians
fn degreesOfRadians(): Fixed
The angle in degrees that this many radians is, rounded to the nearest part.
fn doubled
fn doubled(): Fixed
Twice the value.
fn isCloseTo
fn isCloseTo(other: Fixed, tolerance: Fixed = Fixed(66)): Bool
Whether the two values are within tolerance of each other. The default is about 0.001.
extend Fixed with Power<Int64>
extend Fixed with Power<Int64>
A whole power, by squaring: every step is a multiplication of Fixed values, so the result is the same bits
everywhere, and a negative exponent is one over the positive power.
Panics
Where a step leaves the range a multiplication of two Fixed values has, and at zero to a negative power, which is a
division by zero.
fn power
fn power(exponent: Int64): Fixed
extend Fixed with From<Int64>
extend Fixed with From<Int64>
A whole number is exact as a Fixed while its magnitude stays below 2^47; above that the multiplication panics.
fn from
static fn from(value: Int64): Fixed