f32
stdlib/core/f32.hk: methods on the f32 fixed-width float.
f32 is an IEEE 754 single (HANKI.md §3). It shares the arithmetic operators and comparisons of the fixed-width float tier; this file adds the widening to_f64 bridge and its own min/max/abs (the f64 twins rounded back to f32). Display sits with the other Display impls in display.hk. f32 has no Eq/Ord (IEEE NaN breaks reflexivity), so min and max are intrinsics and not the Ord defaults, as on f64.
impl f32
to_bits
def to_bits(self) -> u32
The raw IEEE 754 bit pattern of self, as a u32. Exact inverse of from_bits.
f32 is stored in an f64 slot at runtime, and this narrows first: the pattern is the one a 32-bit float has and never the low half of the 64-bit one. A value that does not fit f32 was already rounded on the way in. The narrowing leaves a signaling NaN signaling, which the hardware conversion between the two widths would not.
1.0f32.to_bits() => 1065353216u32
0.0f32.to_bits() => 0u32
from_bits
def from_bits(bits: u32) -> f32
The f32 whose IEEE 754 bit pattern is bits. Exact inverse of to_bits, and total: every one of the 2^32 patterns is a float, the NaNs and infinities included: a signaling NaN's payload comes back out as it went in, and a codec re-emitting a float field it never interpreted does not alter it.
Arithmetic is a different matter and remains IEEE: adding to a signaling NaN yields a quiet one, the standard having an arithmetic operation quiet it. What this pair promises is that carrying the value does not.
This is the only way to build an f32 from anything other than a literal or a parse, which is what a generator needs (core/arbitrary).
f32.from_bits(1065353216u32) => 1.0f32
f32.from_bits(0u32) => 0.0f32
f32.from_bits(4290772990u32).to_bits() => 4290772990u32
_parse
def _parse(s: string) -> Option<f32>
The runtime seam under [FromString]: the raw yes-or-no parse, with no room to say why. parse below is the surface, and turns the None into a reason.
to_f64
def to_f64(self) -> f64
Widen to f64: every f32 value is representable as an f64, so the crossing is lossless. This is the bridge the numeric-tower section names for mixing widths: x.to_f64() + y where y: f64.
1.5f32.to_f64() => 1.5f64
(-2.25f32).to_f64() => -2.25f64
min
def min(self, other: f32) -> f32
The smaller of self and other (IEEE minNum: where one is NaN and the other is not, the other is returned).
2.0f32.min(3.0f32) => 2.0f32
3.0f32.min(2.0f32) => 2.0f32
max
def max(self, other: f32) -> f32
The larger of self and other (IEEE maxNum).
2.0f32.max(3.0f32) => 3.0f32
3.0f32.max(2.0f32) => 3.0f32
abs
def abs(self) -> f32
The magnitude of self, the sign bit cleared. (-0.0).abs() is 0.0; NaN.abs() is NaN.
(-2.5f32).abs() => 2.5f32
2.5f32.abs() => 2.5f32
impl FromString<f32>
parse
def parse(s: string) -> Result<f32, ParseError>
Parses a decimal float, correctly rounded. Accepts the forms the platform float parser does (1.5, -0.25, 1e10); a magnitude past f32's range rounds to the IEEE infinity in place of failing, and the only way to fail is for the text not to denote a number at all. Surrounding whitespace is trimmed.
The success doctests compare the unwrapped value and not the Result: f32 has no Eq (IEEE NaN breaks reflexivity), and a Result<f32, ParseError> cannot be ==-compared (HANKI.md §20). The failure line maps the payload to a bool for the same reason. An Err is comparable only when both sides are.
f32.parse("1.5").unwrap_or(0.0f32) => 1.5f32
f32.parse("-0.25").unwrap_or(0.0f32) => -0.25f32
f32.parse("nope").map(|v| v > 0.0f32) => Err(ParseError(input="nope", reason="not a decimal float"))