Reference

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. multiply forms parts * other.parts before it scales back down, so two values above about 46341 overflow and the multiplication panics, although both operands are far inside Fixed.largest.
  • squareRoot scales 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.smallest cannot be shown or negated. Its count of parts is the smallest Int there is, and that one has no positive counterpart - so absolute on 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 a Fixed holds exactly: Fixed.tryFrom("0.1") is 0.100006103515625, and Fixed.show writes that, because that is the number.

Related

  • Real - the trait that makes this and Float64 interchangeable.
  • 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

  • NumberParseError where 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