Writing floats v0.1.0
WriteDecimal
pub func WriteDecimal(
cursor: &var ByteCursor,
value: float64,
significant: uint
)
Provides the public WriteDecimal operation.
Parameters
| Name | Type | Description |
|---|---|---|
cursor | &var ByteCursor | the buffer the digits are written into |
value | float64 | the value to act on |
significant | uint | how 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.
Fields
| Name | Type | Description |
|---|---|---|
significand | uint64 | The integer significand, including the implicit bit for a normal value. Zero only for a zero. |
exponent | int | The power of two the significand is scaled by. |
atBinadeBottom | bool | Whether the lower neighbour is half as far away as the upper one. |
negative | bool | Whether 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.
Parameters
| Name | Type | Description |
|---|---|---|
value | float64 | the 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.
Parameters
| Name | Type | Description |
|---|---|---|
value | float32 | the 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.
Parameters
| Name | Type | Description |
|---|---|---|
value | float16 | the 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.
Parameters
| Name | Type | Description |
|---|---|---|
value | float8 | the 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.
Parameters
| Name | Type | Description |
|---|---|---|
cursor | &var ByteCursor | the destination |
parts | FloatParts | the value taken apart |
scientificFrom | int | the 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.
MaximumFixedLength
pub const MaximumFixedLength: uint = 1424;
The longest a fixed rendering can be: sign, integer digits, point and fraction.
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.
Parameters
| Name | Type | Description |
|---|---|---|
significand | uint64 | the integer significand, which must not be zero |
exponent | int | the power of two it is scaled by |
precision | uint | how many digits after the decimal point are wanted |
into | var char8[..] | where the digits go |
count | *var uint | receives how many digits were written |
power | *var int | receives 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.
Parameters
| Name | Type | Description |
|---|---|---|
cursor | &var ByteCursor | the destination |
parts | FloatParts | the value taken apart |
precision | uint | how 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.
Parameters
| Name | Type | Description |
|---|---|---|
writer | &var TextWriter | the destination |
spec | FormatSpec | what the placeholder asked for, carrying a precision |
parts | FloatParts | the 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.
Parameters
| Name | Type | Description |
|---|---|---|
writer | &var TextWriter | the destination |
spec | FormatSpec | what the placeholder asked for |
parts | FloatParts | the finite value taken apart |
mark | char8 | the 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.
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.
Parameters
| Name | Type | Description |
|---|---|---|
significand | uint64 | the integer significand, which must not be zero |
exponent | int | the power of two it is scaled by |
atBinadeBottom | bool | whether the lower gap is half the upper one |
into | var char8[..] | where the digits go, at least MaximumShortestDigits long |
count | *var uint | receives how many digits were written |
exponent10 | *var int | receives the power of ten the first digit stands for |
Returns: true when the digits were produced