MMgr v1.0.0
Zero-heap memory manager in C11. One borrowed buffer, carved by asserted offset.
Loading...
Searching...
No Matches
transformo.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 transformo.h
9 * @brief Decimal to binary conversion, the limits, the arguments, and the muto dispatch table.
10 * @author dstroy0 (Douglas Quigg) <dquigg123@gmail.com>
11 * @date 2026-08-29
12 *
13 * @note take builds a mantissa one digit at a time.
14 * @note scale turns one into a double, and scale_to_u64 turns one into an integer.
15 */
16#ifndef MMGR_TRANSFORMO_H
17#define MMGR_TRANSFORMO_H
18
19#include "fractio/fractio.h"
20#include "mmgr.h"
21#include "pow5/pow5.h"
22
23EMBED_BEGIN_DECLS
24
25/**
26 * @brief Expands to the largest mantissa mmgr_muto_take will extend, which is (~0 - 9) / 10.
27 *
28 * @note Chosen so multiplying by ten and adding a digit of nine both stay inside an embed_u64.
29 * @note mmgr_muto_take returns EMBED_FALSE instead of appending once the mantissa passes this.
30 * @note Explicit casts hold both the complement and the result at embed_u64, and the 9u and 10u are
31 * suffixed to match, and so the subtraction and the divide are done at the mantissa's own width
32 * and not at int's.
33 */
34#define MMGR_MUTO_MANT_MAX ((embed_u64)((~(embed_u64)0 - 9u) / 10u))
35
36/**
37 * @brief Expands to 400, the count at which a parsed decimal exponent stops accumulating.
38 *
39 * @note cellul_expo tests its running exponent against this before each multiply and add. A long run
40 * of digits cannot run the count away with the embed_iword it is kept in.
41 * @note 400 is past the 308 a double reaches. Anything the clamp holds back has already gone to a
42 * signed infinity or a signed zero, and stopping the count early cannot change the result.
43 * @note The literal is untyped, matching the embed_iword exponent it is compared against.
44 */
45#define MMGR_MUTO_EXP_LIMIT 400
46
47/**
48 * @brief Arguments for the three muto calls.
49 *
50 * @note take reads mant and digit.
51 * @note scale reads mant, ex, rest and neg.
52 * @note scale_to_u64 reads mant, e2 and ex.
53 * @warning mant is written through by take, and so the caller's mantissa changes [BORROWS].
54 */
55typedef struct
56{
57 embed_u64 *const mant; /**< The mantissa, extended by take and read by both scaling calls [BORROWS]. */
58 const char digit; /**< ASCII decimal digit take appends. */
59 const embed_iword e2; /**< Binary exponent the mantissa already carries, read by scale_to_u64. */
60 const embed_iword ex; /**< Decimal exponent to apply. */
61 const embed_iword rest; /**< Non-zero when the caller already dropped bits, and so rounding can see them. */
62 const embed_bool neg; /**< Sign scale gives its result. */
64
65/**
66 * @brief Type of the muto dispatch table.
67 *
68 * @note EMBED_TABLE_LAYOUT asserts the three members sit at consecutive EMBED_FUNCTION_POINTER_BYTES offsets, with
69 * nothing else.
70 */
71typedef struct
72{
73 embed_bool (*take)(const TransformoCfg *args); /**< Appends one decimal digit to a mantissa. */
74 double (*scale)(const TransformoCfg *args); /**< Turns a mantissa and decimal exponent into a double. */
75 embed_u64 (*scale_to_u64)(const TransformoCfg *args); /**< Turns the same plus e2 into a rounded 64-bit integer. */
77EMBED_TABLE_LAYOUT(TransformoNs, take, scale, scale_to_u64);
78
79/**
80 * @brief Appends args->digit to *args->mant as one more decimal digit.
81 *
82 * @param[in] args The mantissa to extend and the digit to append [BORROWS].
83 * @return EMBED_TRUE when the digit was appended, EMBED_FALSE when *args->mant already passed
84 * MMGR_MUTO_MANT_MAX.
85 * @note On EMBED_FALSE the mantissa is left as it was. A caller can count the digits it had to drop.
86 * @warning args->digit must be an ASCII decimal digit, since the value added is args->digit minus '0'.
87 * @warning Writes through args->mant [BORROWS].
88 */
89embed_bool mmgr_muto_take(const TransformoCfg *args);
90
91/**
92 * @brief Turns *args->mant times ten raised to args->ex into a double, signed by args->neg.
93 *
94 * @param[in] args The mantissa, the decimal exponent, the dropped bits and the sign [BORROWS].
95 * @return The nearest double, with ties going to even.
96 * @note Set args->rest when digits were dropped from the mantissa, and so the rounding still accounts for them.
97 * @note Takes a plain double path when nothing was dropped, the mantissa is under 2^53 and args->ex is within 22.
98 * @note Does not read args->e2, and so the mantissa is taken as a plain integer.
99 * @warning Returns a signed infinity for an args->ex above MMGR_POW5_MAX and a signed zero below its negative.
100 */
101double mmgr_muto_scale(const TransformoCfg *args);
102
103/**
104 * @brief Turns *args->mant times two raised to args->e2 times ten raised to args->ex into a rounded 64-bit integer.
105 *
106 * @param[in] args The mantissa, its binary exponent and the decimal exponent [BORROWS].
107 * @return The nearest integer with a half going up, 0 when the value rounds below one, or all
108 * ones when too large.
109 * @note Reads args->e2, which mmgr_muto_scale leaves alone, and so the mantissa may carry a binary exponent here.
110 * @note A half is taken up whatever lies below it, and so the result is the magnitude rounded away from
111 * zero. mmgr_muto_scale rounds its own halves to even, as a double wants and what
112 * this deliberately does not do.
113 * @note Reads neither args->rest, args->above nor args->neg, and so the result is always unsigned.
114 * @warning Does not bound args->ex against MMGR_POW5_MAX the way mmgr_muto_scale does. Only
115 * MMGR_POW5_STEPS bits of the magnitude are walked. A larger exponent loses its high
116 * bits and the value scaled is not the one asked for.
117 */
119
120/**
121 * @brief Dispatch table instance named muto, with each member set to its mmgr_muto_ function.
122 */
123EMBED_TABLE_STORAGE TransformoNs muto EMBED_UNUSED = {
124 .take = mmgr_muto_take,
125 .scale = mmgr_muto_scale,
126 .scale_to_u64 = mmgr_muto_scale_to_u64,
127};
128
129EMBED_END_DECLS
130
131#endif
The binary64 field layout and scale bounds, the assertions that pin them, the six entry points and th...
The embedded_types headers every module builds on.
Powers of five as 128-bit significands, for scaling a decimal mantissa into a binary one.
Arguments for the three muto calls.
Definition transformo.h:56
embed_u64 *const mant
Definition transformo.h:57
const embed_iword ex
Definition transformo.h:60
const char digit
Definition transformo.h:58
const embed_iword e2
Definition transformo.h:59
const embed_iword rest
Definition transformo.h:61
const embed_bool neg
Definition transformo.h:62
Type of the muto dispatch table.
Definition transformo.h:72
embed_bool(*) take(const TransformoCfg *args)
Definition transformo.h:73
embed_u64(*) scale_to_u64(const TransformoCfg *args)
Definition transformo.h:75
double(*) scale(const TransformoCfg *args)
Definition transformo.h:74
embed_u64 mmgr_muto_scale_to_u64(const TransformoCfg *args)
Turns *args->mant times two raised to args->e2 times ten raised to args->ex into a rounded 64-bit int...
static const TransformoNs muto
Dispatch table instance named muto, with each member set to its mmgr_muto_ function.
Definition transformo.h:123
embed_bool mmgr_muto_take(const TransformoCfg *args)
Appends args->digit to *args->mant as one more decimal digit.
double mmgr_muto_scale(const TransformoCfg *args)
Turns *args->mant times ten raised to args->ex into a double, signed by args->neg.