MMgr v1.0.0
Zero-heap memory manager in C11. One borrowed buffer, carved by asserted offset.
Loading...
Searching...
No Matches
fractio.h
Go to the documentation of this file.
1/* MMgr - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2 * SPDX-License-Identifier: AGPL-3.0-or-later OR LicenseRef-Commercial OR LicenseRef-Educational
3 *
4 * Every use falls under AGPL-3.0-or-later unless you hold explicit permission, which is either a
5 * negotiated commercial licensing contract or an educator's license issued to you personally.
6 */
7/**
8 * @file fractio.h
9 * @brief The binary64 field layout and scale bounds, the assertions that pin them, the six entry points
10 * and the fract table.
11 * @author dstroy0 (Douglas Quigg) <dquigg123@gmail.com>
12 * @date 2026-08-29
13 *
14 * @note Everything here is written for binary64. The assertions below are what hold the target to it,
15 * and they are EMBED_STATIC_ASSERT. A target whose double is another shape fails the build
16 * instead of reading the wrong bits.
17 * @note FractioCfg carries no pointer, and so no call here follows one into a caller's buffer. The whole
18 * module is field arithmetic on the one value it is handed.
19 * @warning args is the single one pointer the six entry points take, and not one of them tests it. The bodies
20 * EMBED_ENTRY writes read its members straight through. A null args is dereferenced.
21 */
22#ifndef MMGR_FRACTIO_H
23#define MMGR_FRACTIO_H
24
25#include "mmgr.h"
26
27EMBED_BEGIN_DECLS
28
29/**
30 * @brief The binary64 field layout: three masks, the sign shift, the two field widths, the two whole
31 * field values and the exponent bias.
32 *
33 * @note The masks, the shift and the widths give each field's position and extent. MMGR_DBL_SIGN_ONE
34 * and MMGR_DBL_EXP_ALL are values a field can hold and not positions, and MMGR_DBL_BIAS is
35 * an amount.
36 * @note The assertions below check the masks tile the word without gap or overlap.
37 * @note The masks, MMGR_DBL_SIGN_ONE and MMGR_DBL_EXP_ALL carry a ull suffix, matching the embed_u64 the
38 * fields are read from. The shifts and widths carry u. MMGR_DBL_BIAS carries neither, since it is
39 * subtracted in the signed scale arithmetic below.
40 */
41#define MMGR_DBL_SIGN_MASK 0x8000000000000000ull /**< The sign bit. */
42#define MMGR_DBL_EXP_MASK 0x7FF0000000000000ull /**< The eleven exponent bits. */
43#define MMGR_DBL_MANT_MASK 0x000FFFFFFFFFFFFFull /**< The fifty-two stored mantissa bits. */
44#define MMGR_DBL_SIGN_SHIFT 63u /**< Bit position of the sign. */
45#define MMGR_DBL_MANT_BITS 52u /**< Stored mantissa width, and the exponent's shift. */
46#define MMGR_DBL_EXP_BITS 11u /**< Exponent width. */
47#define MMGR_DBL_SIGN_ONE 0x1ull /**< A sign of one, and merge's mask for args->sign. */
48#define MMGR_DBL_EXP_ALL 0x7FFull /**< All ones in the exponent, and merge's mask for args->exp. */
49#define MMGR_DBL_BIAS 1023 /**< Amount added to the true exponent when stored. */
50
51/**
52 * @brief The width of a double in bits, bytes and embed_word units.
53 *
54 * @note MMGR_DBL_BITS is declared and not measured. The assertion below compares it against sizeof(double)
55 * and fails the build when the target disagrees.
56 * @note The 8u in MMGR_DBL_BYTES is bits per byte. That same assertion holds a target to it, since a
57 * byte of another width would leave sizeof(double) * 8u short of MMGR_DBL_BITS.
58 * @note MMGR_DBL_WORDS rounds up, and the assertion below requires it to come out exact.
59 */
60#define MMGR_DBL_BITS 64u /**< Bits in a double. */
61#define MMGR_DBL_BYTES (MMGR_DBL_BITS / 8u) /**< Bytes in a double. */
62#define MMGR_DBL_WORDS \
63 ((MMGR_DBL_BITS + (EMBED_WORD_BITS - 1u)) / EMBED_WORD_BITS) /**< embed_word units per double. \
64 */
65
66/**
67 * @brief Pins the target's double to the width and storage this file assumes.
68 *
69 * @note Checks the bit width, that a double is a whole number of embed_word units, and that embed_u64 holds it.
70 */
71EMBED_STATIC_ASSERT(sizeof(double) * 8u == MMGR_DBL_BITS,
72 "double is not 64 bits on this target, so every field position in this file is wrong "
73 "- a build where double means float cannot use it");
74EMBED_STATIC_ASSERT(MMGR_DBL_WORDS *EMBED_WORD_BITS == MMGR_DBL_BITS,
75 "a double is not a whole number of words on this target");
76EMBED_STATIC_ASSERT(sizeof(embed_u64) == sizeof(double), "the bit pattern of a double does not fit embed_u64");
77
78/**
79 * @brief Pins the field constants above against each other.
80 *
81 * @note Checks the widths sum to 64, the masks tile the word, and no two masks overlap.
82 * @note Also checks each mask against the shift or width for the same field, that MMGR_DBL_EXP_ALL fills
83 * MMGR_DBL_EXP_BITS, and that MMGR_DBL_BIAS is half the exponent range less one.
84 */
85EMBED_STATIC_ASSERT(1u + MMGR_DBL_EXP_BITS + MMGR_DBL_MANT_BITS == MMGR_DBL_BITS,
86 "the three fields do not add up to the width of the value");
87EMBED_STATIC_ASSERT((MMGR_DBL_SIGN_MASK | MMGR_DBL_EXP_MASK | MMGR_DBL_MANT_MASK) == 0xFFFFFFFFFFFFFFFFull,
88 "the three field masks leave a gap");
89EMBED_STATIC_ASSERT((MMGR_DBL_SIGN_MASK & MMGR_DBL_EXP_MASK) == 0u && (MMGR_DBL_EXP_MASK & MMGR_DBL_MANT_MASK) == 0u &&
91 "the three field masks overlap");
93 "the sign mask and the sign shift disagree about where the sign is");
94EMBED_STATIC_ASSERT(MMGR_DBL_EXP_MASK == (MMGR_DBL_EXP_ALL << MMGR_DBL_MANT_BITS),
95 "the exponent mask and the exponent width disagree");
96EMBED_STATIC_ASSERT(MMGR_DBL_EXP_ALL == ((1u << MMGR_DBL_EXP_BITS) - 1u), "the exponent does not fill its field");
97EMBED_STATIC_ASSERT(MMGR_DBL_BIAS == ((1 << (MMGR_DBL_EXP_BITS - 1u)) - 1), "the bias is not the one binary64 uses");
98
99/**
100 * @brief The range of powers of two a double can carry once the mantissa is treated as an integer.
101 *
102 * @note MMGR_DBL_SCALE_MAX takes the largest finite exponent, removes the bias, and drops the mantissa width.
103 * @note MMGR_DBL_SCALE_MIN starts from an exponent field of 1, the smallest normal.
104 * @note Explicit casts hold both expressions in embed_iword, since MMGR_DBL_EXP_ALL and MMGR_DBL_MANT_BITS
105 * are unsigned. MMGR_DBL_SCALE_MIN is negative, and without its cast the subtraction would wrap.
106 */
107#define MMGR_DBL_SCALE_MAX ((embed_iword)(MMGR_DBL_EXP_ALL - 1u) - MMGR_DBL_BIAS - (embed_iword)MMGR_DBL_MANT_BITS)
108#define MMGR_DBL_SCALE_MIN (1 - MMGR_DBL_BIAS - (embed_iword)MMGR_DBL_MANT_BITS)
109
110/**
111 * @brief Pins the two scale bounds to the values binary64 gives.
112 */
113EMBED_STATIC_ASSERT(MMGR_DBL_SCALE_MAX == 971, "the largest scale a finite double can carry is not what it was");
114EMBED_STATIC_ASSERT(MMGR_DBL_SCALE_MIN == -1074, "the smallest scale a subnormal can carry is not what it was");
115
116/**
117 * @brief Arguments for the fract calls, where each call reads only the members it needs.
118 *
119 * @note val and bits share storage, and so writing one and reading the other reinterprets the same bytes.
120 * @note sign, exp and mant are read by merge alone. The other five calls read the union.
121 */
122typedef struct
123{
124 union {
125 const double val; /**< The value, when the caller supplies a double. */
126 const embed_u64 bits; /**< The same storage read as a bit pattern. */
127 };
128 const embed_u64 sign; /**< Sign for merge, 0 or 1. */
129 const embed_u64 exp; /**< Biased exponent for merge. */
130 const embed_u64 mant; /**< Stored mantissa for merge. */
131} FractioCfg;
132
133/**
134 * @brief Type of the fract dispatch table.
135 *
136 * @note EMBED_TABLE_LAYOUT asserts the six members sit at consecutive EMBED_FUNCTION_POINTER_BYTES offsets, with
137 * nothing else.
138 */
139typedef struct
140{
141 embed_u64 (*sign)(const FractioCfg *args); /**< Sign bit of bits, as 0 or 1. */
142 embed_u64 (*exp)(const FractioCfg *args); /**< Biased exponent field of bits. */
143 embed_u64 (*mant)(const FractioCfg *args); /**< Stored mantissa field of bits. */
144 embed_u64 (*merge)(const FractioCfg *args); /**< Packs sign, exp and mant into one pattern. */
145 double (*from_bits)(const FractioCfg *args); /**< Reads the union as a double. */
146 embed_u64 (*to_bits)(const FractioCfg *args); /**< Reads the union as a bit pattern. */
147} FractioNs;
148EMBED_TABLE_LAYOUT(FractioNs, sign, exp, mant, merge, from_bits, to_bits);
149
150/**
151 * @brief Returns the sign bit of args->bits.
152 *
153 * @param[in] args Bit pattern in the union [BORROWS].
154 * @return 0 for a positive sign, 1 for a negative one.
155 * @note The bit is read as stored, alone is examined. A negative zero answers 1, and so
156 * does a NaN carrying the sign.
157 */
158embed_u64 mmgr_fract_sign(const FractioCfg *args);
159
160/**
161 * @brief Returns the exponent field of args->bits.
162 *
163 * @param[in] args Bit pattern in the union [BORROWS].
164 * @return The stored exponent, still carrying MMGR_DBL_BIAS.
165 * @note 0 marks a zero or subnormal. MMGR_DBL_EXP_ALL marks an infinity or NaN.
166 */
167embed_u64 mmgr_fract_exp(const FractioCfg *args);
168
169/**
170 * @brief Returns the mantissa field of args->bits.
171 *
172 * @param[in] args Bit pattern in the union [BORROWS].
173 * @return The fifty-two stored bits, without the implicit leading one.
174 * @note The leading one is implicit only where the exponent field is nonzero. A zero or a subnormal
175 * has none, and there these fifty-two bits are the whole mantissa.
176 */
177embed_u64 mmgr_fract_mant(const FractioCfg *args);
178
179/**
180 * @brief Packs args->sign, args->exp and args->mant into one bit pattern.
181 *
182 * @param[in] args The three fields [BORROWS].
183 * @return The assembled pattern.
184 * @note Each field is masked to its own width. A wide input cannot reach a neighboring field.
185 * @warning What the mask drops is gone and nothing reports it. A sign above MMGR_DBL_SIGN_ONE, an
186 * exponent above MMGR_DBL_EXP_ALL or a mantissa above MMGR_DBL_MANT_MASK keeps only its low
187 * bits.
188 */
189embed_u64 mmgr_fract_merge(const FractioCfg *args);
190
191/**
192 * @brief Reads the union as a double.
193 *
194 * @param[in] args Union with its bits member filled [BORROWS].
195 * @return The same storage interpreted as a double.
196 */
198
199/**
200 * @brief Reads the union as a bit pattern.
201 *
202 * @param[in] args Union with its val member filled [BORROWS].
203 * @return The same storage interpreted as embed_u64.
204 */
205embed_u64 mmgr_fract_to_bits(const FractioCfg *args);
206
207/**
208 * @brief Dispatch table instance named fract, whose members are the mmgr_fract_ entries.
209 *
210 * @note EMBED_TABLE_STORAGE is static const, and so every translation unit including this header holds a copy of its
211 * own and the addresses differ between them. The six functions it points at are the one shared
212 * definition.
213 * @note EMBED_UNUSED keeps a translation unit that includes the header without calling through the
214 * table from warning about it. It expands to nothing where the unused attribute is unavailable.
215 */
216EMBED_TABLE_STORAGE FractioNs fract EMBED_UNUSED = {
217 .sign = mmgr_fract_sign,
218 .exp = mmgr_fract_exp,
219 .mant = mmgr_fract_mant,
220 .merge = mmgr_fract_merge,
221 .from_bits = mmgr_fract_from_bits,
222 .to_bits = mmgr_fract_to_bits,
223};
224
225EMBED_END_DECLS
226
227#endif
embed_u64 mmgr_fract_merge(const FractioCfg *args)
Packs args->sign, args->exp and args->mant into one bit pattern.
#define MMGR_DBL_EXP_ALL
Definition fractio.h:48
embed_u64 mmgr_fract_mant(const FractioCfg *args)
Returns the mantissa field of args->bits.
#define MMGR_DBL_MANT_BITS
Definition fractio.h:45
#define MMGR_DBL_BITS
The width of a double in bits, bytes and embed_word units.
Definition fractio.h:60
embed_u64 mmgr_fract_to_bits(const FractioCfg *args)
Reads the union as a bit pattern.
#define MMGR_DBL_BIAS
Definition fractio.h:49
#define MMGR_DBL_EXP_MASK
Definition fractio.h:42
embed_u64 mmgr_fract_sign(const FractioCfg *args)
Returns the sign bit of args->bits.
embed_u64 mmgr_fract_exp(const FractioCfg *args)
Returns the exponent field of args->bits.
double mmgr_fract_from_bits(const FractioCfg *args)
Reads the union as a double.
#define MMGR_DBL_MANT_MASK
Definition fractio.h:43
static const FractioNs fract
Dispatch table instance named fract, whose members are the mmgr_fract_ entries.
Definition fractio.h:216
#define MMGR_DBL_SIGN_SHIFT
Definition fractio.h:44
#define MMGR_DBL_EXP_BITS
Definition fractio.h:46
#define MMGR_DBL_SIGN_ONE
Definition fractio.h:47
#define MMGR_DBL_SIGN_MASK
The binary64 field layout: three masks, the sign shift, the two field widths, the two whole field val...
Definition fractio.h:41
#define MMGR_DBL_WORDS
Definition fractio.h:62
#define MMGR_DBL_SCALE_MAX
Pins the target's double to the width and storage this file assumes.
Definition fractio.h:107
The embedded_types headers every module builds on.
Pins the two scale bounds to the values binary64 gives.
Definition fractio.h:123
const embed_u64 exp
Definition fractio.h:129
const embed_u64 mant
Definition fractio.h:130
const embed_u64 sign
Definition fractio.h:128
Type of the fract dispatch table.
Definition fractio.h:140
embed_u64(*) to_bits(const FractioCfg *args)
Definition fractio.h:146
embed_u64(*) mant(const FractioCfg *args)
Definition fractio.h:143
embed_u64(*) exp(const FractioCfg *args)
Definition fractio.h:142
double(*) from_bits(const FractioCfg *args)
Definition fractio.h:145
embed_u64(*) sign(const FractioCfg *args)
Definition fractio.h:141
embed_u64(*) merge(const FractioCfg *args)
Definition fractio.h:144