Writing floats v0.1.0

Source
The shortest, fixed-precision and scientific renderings of a float32 or float64.

WriteDecimal

pub func WriteDecimal(
    cursor: &var ByteCursor,
    value: float64,
    significant: uint
)

Provides the public WriteDecimal operation.

Source

Parameters

NameTypeDescription
cursor&var ByteCursorthe buffer the digits are written into
valuefloat64the value to act on
significantuinthow many significant digits to write

FloatParts

pub struct FloatParts {
    pub significand: uint64;
    pub exponent: int;
    pub atBinadeBottom: bool;
    pub negative: bool;
}

A finite float taken apart: the value is significand * 2^exponent. Copy, carrying no reference to the value it came from.

Source

Fields

NameTypeDescription
significanduint64The integer significand, including the implicit bit for a normal value. Zero only for a zero.
exponentintThe power of two the significand is scaled by.
atBinadeBottomboolWhether the lower neighbour is half as far away as the upper one.
negativeboolWhether the value is negative, including the negative zero.

DecomposeFloat64

pub func DecomposeFloat64(value: float64) -> FloatParts

Takes a float64 apart into the significand and exponent its digits are generated from. A NaN or an infinity has no decimal and is not this function's business; a caller establishes the value is finite first. A zero comes back with a zero significand, which the caller writes as 0 without generating anything. Cannot fail; runs in constant time and allocates nothing.

Source

Parameters

NameTypeDescription
valuefloat64the finite float to take apart

Returns: its significand, exponent, binade position and sign

DecomposeFloat32

pub func DecomposeFloat32(value: float32) -> FloatParts

Takes a float32 apart into the significand and exponent its digits are generated from. The same decomposition at the narrower width, so both widths reach one generator. Cannot fail; runs in constant time and allocates nothing.

Source

Parameters

NameTypeDescription
valuefloat32the finite float to take apart

Returns: its significand, exponent, binade position and sign

DecomposeFloat16

pub func DecomposeFloat16(value: float16) -> FloatParts

Takes a float16 apart into the significand and exponent its digits are generated from. IEEE 754 binary16: eleven bits of significand and an exponent biased by fifteen. The neighbours the generator measures against are this width's own, so the digits that come back are the shortest that read back as a float16, not as the float32 or float64 the value would widen to. Cannot fail; runs in constant time and allocates nothing.

Source

Parameters

NameTypeDescription
valuefloat16the finite float to take apart

Returns: its significand, exponent, binade position and sign

DecomposeFloat8

pub func DecomposeFloat8(value: float8) -> FloatParts

Takes a float8 apart into the significand and exponent its digits are generated from. The E4M3 layout with IEEE-style specials: four bits of significand, an exponent biased by seven, and the all-ones exponent kept for the infinities and NaNs, so the largest finite value is 240. As with the other widths, the neighbours are this width's own. Cannot fail; runs in constant time and allocates nothing.

Source

Parameters

NameTypeDescription
valuefloat8the finite float to take apart

Returns: its significand, exponent, binade position and sign

WriteShortestFloat

pub func WriteShortestFloat(
    cursor: &var ByteCursor,
    parts: FloatParts,
    scientificFrom: int
) -> bool

Writes the shortest decimal text of a finite, non-zero float taken apart by DecomposeFloat64 or its narrow counterpart, choosing fixed or scientific notation the way the rest of this package does. The sign is written from parts, so a negative zero keeps its sign even though its digits are a bare zero. Answers false when the exact arithmetic could not run in the fixed workspace, which Docs/NarrowFloat.md proves the narrow widths do not reach. A caller that sees it has a defect in the bound rather than a value to handle.

Source

Parameters

NameTypeDescription
cursor&var ByteCursorthe destination
partsFloatPartsthe value taken apart
scientificFromintthe decimal exponent at or above which scientific notation is used

Returns: true when the whole rendering was written

MaximumPrecision

pub const MaximumPrecision: uint = 1100;

The largest precision a narrow float renders at. Eleven hundred, which is exactly enough rather than round: the smallest float64 subnormal is 2^-1074 and its exact decimal has 1,074 fractional digits, the largest finite value has 309 integer ones, and no float64 has anything to say past that. A request beyond this is refused rather than clamped, because a caller asking for five thousand digits has miscalculated something and answering with 3,900 zeros would hide it.

Source

MaximumFixedLength

pub const MaximumFixedLength: uint = 1424;

The longest a fixed rendering can be: sign, integer digits, point and fraction.

Source

PrecisionDigits

pub func PrecisionDigits(
    significand: uint64,
    exponent: int,
    precision: uint,
    into: var char8[..],
    count: *var uint,
    power: *var int
) -> bool

Writes the digits of significand * 2^exponent down to the place precision names, rounded to nearest with ties to even. The digits are the significant ones with no point and no sign, and power receives the exponent that places them: the value is 0.d1d2...dn * 10^power, so the last digit written stands for 10^-precision. A value small enough that nothing reaches that place produces no digits and a power of zero, which the caller writes as a zero and a tail of zeros. Answers false when precision is past MaximumPrecision, when into is too short for the digits the value needs, or when the exact arithmetic could not run in the fixed workspace — which Docs/NarrowFloat.md proves the narrow widths do not reach.

Source

Parameters

NameTypeDescription
significanduint64the integer significand, which must not be zero
exponentintthe power of two it is scaled by
precisionuinthow many digits after the decimal point are wanted
intovar char8[..]where the digits go
count*var uintreceives how many digits were written
power*var intreceives the power of ten that places them

Returns: true when the digits were produced

WritePrecisionFloat

pub func WritePrecisionFloat(
    cursor: &var ByteCursor,
    parts: FloatParts,
    precision: uint
) -> bool

Writes a finite float in fixed notation with exactly precision digits after the decimal point. Always fixed notation, because that is the only notation in which "digits after the decimal point" has one meaning. A large value therefore renders with every integer digit it has and then the fraction — which is what was asked for, and why the precision is bounded rather than the output. Answers false when the precision is past MaximumPrecision, or when the exact arithmetic could not run in the fixed workspace. There is no fallback to another rendering: a refused request is refused.

Source

Parameters

NameTypeDescription
cursor&var ByteCursorthe destination
partsFloatPartsthe value taken apart
precisionuinthow many digits after the decimal point

Returns: true when the whole rendering was written

WritePrecision

pub func WritePrecision(
    writer: &var TextWriter,
    spec: FormatSpec,
    parts: FloatParts
) -> ! FormatError

Renders parts under a specification that carries a precision, and applies the specification's sign, width, fill and alignment to the result. The large fixed buffer a precision can need lives in this function rather than in the callers', so a placeholder with no precision never pays for it. Reports UnsupportedRequest for a precision past MaximumPrecision, and whatever the writer reported otherwise. There is no fallback: a refused precision produces no output.

Source

Parameters

NameTypeDescription
writer&var TextWriterthe destination
specFormatSpecwhat the placeholder asked for, carrying a precision
partsFloatPartsthe value taken apart

Returns: success when the whole rendering was accepted

WriteScientificNotation

pub func WriteScientificNotation(
    writer: &var TextWriter,
    spec: FormatSpec,
    parts: FloatParts,
    mark: char8
) -> ! FormatError

Renders a finite float in scientific notation under spec, and applies the specification's sign, width, fill and alignment to the result. One digit comes before the point and the rest after it, then mark and the decimal exponent with its sign and at least two digits: 1.2345e+05. Without a precision the digits are the shortest that read back as the same value, with .0 after a lone digit; with one, the precision counts the digits after the point, rounded to nearest with ties to even, and .0 writes no point at all. A zero is 0.0e+00, keeping the sign of a negative zero. Reports UnsupportedRequest for a precision past MaximumPrecision, and whatever the writer reported otherwise. There is no fallback: a refused precision produces no output.

Source

Parameters

NameTypeDescription
writer&var TextWriterthe destination
specFormatSpecwhat the placeholder asked for
partsFloatPartsthe finite value taken apart
markchar8the exponent mark, e or E

Returns: success when the whole rendering was accepted

MaximumShortestDigits

pub const MaximumShortestDigits: uint = 17;

The most digits a shortest rendering can need. Seventeen for a float64 and nine for a float32: those are the counts at which every value of the width is distinguishable from every other, which is the theoretical bound on how long a shortest form can be. The buffer a caller supplies is checked against this rather than against a guess.

Source

ShortestDigits

pub func ShortestDigits(
    significand: uint64,
    exponent: int,
    atBinadeBottom: bool,
    into: var char8[..],
    count: *var uint,
    exponent10: *var int
) -> bool

Writes the shortest decimal digits of significand * 2^exponent that read back to exactly that value. The digits are the significant ones with no point and no sign: into receives them most significant first, count how many there are, and exponent10 the power of ten the first of them stands for, so the value is 0.d1d2...dn * 10^(exponent10 + 1) and the caller places the point. atBinadeBottom says the value is the smallest significand of its exponent, whose lower neighbour is half as far away as its upper one. A caller reading a float64 passes true when the stored mantissa is zero and the biased exponent is above one. Answers false when into is shorter than MaximumShortestDigits, or when the workspace could not hold the arithmetic — which Docs/NarrowFloat.md proves the narrow widths do not reach, so a false from that cause is a defect in the bound rather than an input to handle. Nothing partial is reported: count is zero on failure.

Source

Parameters

NameTypeDescription
significanduint64the integer significand, which must not be zero
exponentintthe power of two it is scaled by
atBinadeBottomboolwhether the lower gap is half the upper one
intovar char8[..]where the digits go, at least MaximumShortestDigits long
count*var uintreceives how many digits were written
exponent10*var intreceives the power of ten the first digit stands for

Returns: true when the digits were produced