| name | new-number-type |
| description | Scaffold a new number system type with all required files, CMake wiring, exception hierarchy, traits, tests, and numeric_limits. Use when adding a new arithmetic type to the Universal library. |
| user-invocable | true |
| argument-hint | <type-name> [template-params...] |
| allowed-tools | Read, Edit, Write, Glob, Grep, Bash |
Scaffold a New Number System Type
Create all required files, CMake wiring, and test structure for a new number type in the Universal library.
Arguments
$ARGUMENTS — the type name (e.g., takum) and optionally a description of the template parameters. If not provided, ask the user.
Before You Start
-
Ask the user:
- How many template parameters? (1-param like
integer<nbits> or 2-param like posit<nbits, es>)
- Is this a static (fixed-size) or elastic (adaptive) type?
- What category? (integer, fixed-point, float, tapered, logarithmic, block format)
- What internal building blocks does it use? (blockbinary, blocksignificand, blocktriple, etc.)
-
Read the matching skeleton template to use as the structural reference:
- 1-param:
include/sw/universal/number/skeleton_1param/
- 2-param:
include/sw/universal/number/skeleton_2params/
-
Read an existing similar type for behavioral reference (e.g., posit for tapered, cfloat for float, fixpnt for fixed-point).
CRITICAL Rules
These are hard-won lessons from past incidents. Violating them causes build failures or CI rejections.
Triviality
- Number types MUST be trivially constructible
- NO in-class member initializers: use
uint8_t _bits; NOT uint8_t _bits{ 0 };
ReportTrivialityOfType<T>() will static_assert fail if the type isn't trivial
- Default constructor must have empty body or be
= default
Constexpr
- Do NOT mark constructors/assignment operators
constexpr if they call std::frexp, std::ldexp, std::log2, etc.
- Only use
constexpr if the conversion path uses only std::memcpy or integer arithmetic
Exception Hierarchy
Portability
- Never use
long double manual bit-shift division — use std::ldexp(1.0l, exponent) instead
- Always initialize
blockbinary temporaries (clang doesn't zero stack like gcc)
- Test with BOTH gcc AND clang before committing
File Creation Order
Create files in this exact order (dependencies flow top-to-bottom):
Step 1: Header files in include/sw/universal/number/TYPE/
| File | Purpose | Key contents |
|---|
TYPE_fwd.hpp | Forward declarations + type aliases | template<params> class TYPE; + convenience aliases |
exceptions.hpp | Exception hierarchy | TYPE_arithmetic_exception, TYPE_divide_by_zero, TYPE_internal_exception |
TYPE_impl.hpp | Main class implementation | Full class with constructors, operators, conversions |
numeric_limits.hpp | std::numeric_limits specialization | All required constants and static functions |
manipulators.hpp | type_tag(), to_binary(), color_print(), range() | Use enable_if_t<is_TYPE<T>> pattern |
attributes.hpp | Free functions for type properties | sign(), scale(), TYPE_range() |
TYPE.hpp | Umbrella header | Includes everything in correct order (see below) |
Umbrella header include order (MUST follow this sequence):
1. Compiler directives (compiler.hpp, architecture.hpp, bit_cast.hpp, long_double.hpp)
2. Required stdlib (<iostream>, <iomanip>)
3. Behavioral compilation switches (TYPENAME_THROW_ARITHMETIC_EXCEPTION, etc.)
4. Exception config forwarding to building blocks
5. Trait function headers (number_traits.hpp, arithmetic_traits.hpp)
6. exceptions.hpp
7. TYPE_fwd.hpp
8. TYPE_impl.hpp
9. TYPE_traits.hpp (from traits/ directory)
10. numeric_limits.hpp
11. manipulators.hpp
12. attributes.hpp
13. mathlib.hpp (if applicable)
Step 2: Traits file in include/sw/universal/traits/
| File | Purpose |
|---|
TYPE_traits.hpp | is_TYPE trait, is_TYPE_trait struct, enable_if_TYPE alias |
The trait MUST match the exact template parameters of the class.
Step 3: Test directory structure
Create the test directory under the appropriate category. The repo uses
static/<CATEGORY>/<TYPE>/ where CATEGORY groups related types:
| Category | Types |
|---|
tapered/ | posit, takum, unum2 |
float/ | cfloat, bfloat16, dfloat, hfloat, e8m0 |
logarithmic/ | lns, dbns |
fixpnt/ | binary, decimal |
integer/ | binary, decimal, octal, hexadecimal |
block/ | microfloat, mxblock, nvblock |
static/<CATEGORY>/TYPE/ (or elastic/TYPE/ for adaptive types)
CMakeLists.txt
api/
api.cpp # Primary API test — start here
conversion/
(empty initially)
logic/
(empty initially)
arithmetic/
(empty initially)
math/
(empty initially)
complex/
(empty initially, only populated when BUILD_COMPLEX=ON)
Step 4: Test CMakeLists.txt
Use the standard pattern. The compile_all label path follows:
"Number Systems/static/<NUMBER_CATEGORY>/<ENCODING>/TYPE/<testdir>"
Check an existing sibling type's CMakeLists.txt for the exact label prefix.
| Number category | Encoding | Label prefix example |
|---|
floating-point | binary | cfloat, bfloat16, dd, microfloat |
floating-point | decimal | dfloat |
floating-point | hexadecimal | hfloat |
floating-point | logarithmic | lns, dbns |
floating-point | tapered | posit, takum |
fixed-point | binary | fixpnt |
fixed-point | decimal | dfixpnt |
integer | binary | integer |
integer | decimal | dint |
rational | binary | rational |
file(GLOB API_SRC "api/*.cpp")
file(GLOB CONVERSION_SRC "conversion/*.cpp")
file(GLOB LOGIC_SRC "logic/*.cpp")
file(GLOB ARITHMETIC_SRC "arithmetic/*.cpp")
file(GLOB MATH_SRC "math/*.cpp")
file(GLOB COMPLEX_SRC "complex/*.cpp")
# Example for lns: "Number Systems/static/floating-point/logarithmic/lns/api"
# Example for fixpnt: "Number Systems/static/fixed-point/binary/fixpnt/api"
# Example for dint: "Number Systems/static/integer/decimal/dint/api"
compile_all("true" "TYPE" "Number Systems/static/<NUMBER_CATEGORY>/<ENCODING>/TYPE/api" "${API_SRC}")
compile_all("true" "TYPE" "Number Systems/static/<NUMBER_CATEGORY>/<ENCODING>/TYPE/conversion" "${CONVERSION_SRC}")
compile_all("true" "TYPE" "Number Systems/static/<NUMBER_CATEGORY>/<ENCODING>/TYPE/logic" "${LOGIC_SRC}")
compile_all("true" "TYPE" "Number Systems/static/<NUMBER_CATEGORY>/<ENCODING>/TYPE/arithmetic" "${ARITHMETIC_SRC}")
compile_all("true" "TYPE" "Number Systems/static/<NUMBER_CATEGORY>/<ENCODING>/TYPE/math" "${MATH_SRC}")
compile_all("true" "TYPE" "Number Systems/static/<NUMBER_CATEGORY>/<ENCODING>/TYPE/complex" "${COMPLEX_SRC}")
Step 5: CMake wiring in root CMakeLists.txt
4 insertion points (find the right alphabetical position among existing types):
-
Option definition (~line 160):
option(UNIVERSAL_BUILD_NUMBER_TYPE "Set to ON to build TYPE tests" OFF)
-
UNIVERSAL_BUILD_NUMBER_STATICS cascade (~line 831):
set(UNIVERSAL_BUILD_NUMBER_TYPE ON)
-
add_subdirectory block (~line 974):
if(UNIVERSAL_BUILD_NUMBER_TYPE)
add_subdirectory("static/<CATEGORY>/TYPE")
endif(UNIVERSAL_BUILD_NUMBER_TYPE)
-
CI_LITE cascade (~line 756, optional — only if portability-critical):
set(UNIVERSAL_BUILD_NUMBER_TYPE ON)
Step 6: Initial api.cpp test
Create a minimal test that:
- Includes the umbrella header
- Sets
TYPENAME_THROW_ARITHMETIC_EXCEPTION 1
- Tests default construction
- Tests construction from native types (int, float, double)
- Tests
type_tag() output
- Tests
ReportTrivialityOfType<TYPE<config>>()
- Uses
ReportTestSuiteHeader() / ReportTestSuiteResults() pattern
- Has full exception catch blocks
Template Parameter Naming Conventions
| Parameter | Name | Notes |
|---|
| Total bits | nbits | NOT N or bits |
| Exponent bits | es | NOT E or exponent_bits |
| Fraction bits | rbits or fbits | Depends on type |
| Block type | bt | Default to uint8_t |
| In friend declarations | nnbits, nes, nbt | Prefix with n |
Verification Checklist
After creating all files:
- Build with gcc:
cmake --build --preset gcc-debug --target TYPE_api
- Run the test:
build/gcc-debug/static/TYPE/TYPE_api
- Build with clang:
cmake --build --preset clang-debug --target TYPE_api
- Run the clang test:
build/clang-debug/static/TYPE/TYPE_api
- Verify triviality passes (no
static_assert failures)
- Verify
type_tag() produces expected output
Reference Implementations
For behavioral reference, read an existing type that's similar:
| If your type is... | Study this implementation |
|---|
| Tapered floating-point | posit/posit_impl.hpp |
| Classic floating-point | cfloat/cfloat_impl.hpp |
| Fixed-point | fixpnt/fixpnt_impl.hpp |
| Logarithmic | lns/lns_impl.hpp |
| Integer | integer/integer_impl.hpp |
| Block format | mxblock/mxblock_impl.hpp |
| Double-double | dd/dd_impl.hpp |