-
-
Notifications
You must be signed in to change notification settings - Fork 19
full domain wide arithmetic
Fixed64 has a deliberately compact Q32.32 range. A complete expression can
still have a valid Fixed64 answer even when a product, difference, or squared
distance in the middle is much larger.
FixedMathSharp's internal Wide layer keeps those intermediates exact until the
public API needs one final result. You continue to work with Fixed64, vectors,
transforms, and geometry; the wide representation stays internal.
Every overloaded C# operator finishes before the next operator begins. It rounds and, when required by the operator contract, saturates its own result.
Fixed64 value = new Fixed64(65_536);
Fixed64 chained = (value * value) / value;
bool succeeded = Fixed64.TryMultiplyDivide(
value,
value,
value,
out Fixed64 fused);The multiplication in chained saturates before division can cancel the
growth. The fused call retains the complete ratio: succeeded is true and
fused == value.
Wide arithmetic also prevents premature rounding. A small product may round to
zero as a standalone Fixed64 even when a later division would restore a
representable raw unit.
| API shape | What happens | Use it when... |
|---|---|---|
| Ordinary operators | Each operator rounds and saturates independently | Every intermediate is a meaningful public value |
| Fused value-returning methods | The expression rounds once; only the final result saturates | A saturated final answer is acceptable |
Try* methods |
Report the member's documented query or output failure without returning a partial result | The caller must distinguish success from that failure |
| Strict constructors/properties | Throw when the required public value cannot be represented | Saturation would misdescribe the value |
*ClippedToDomain geometry APIs |
Clip a conceptual shape only at the public coordinate domain | You explicitly want the representable portion of a shape |
Try does not mean “never throws.” Invalid arguments and violated input
contracts—such as a required normalized axis or nonnegative radius—may still
throw. The API reference documents
the exact failure and exception behavior of each member.
bool representable = Fixed64.TryMultiplyAdd(
Fixed64.MaxValue,
Fixed64.Two,
-Fixed64.MaxValue,
out Fixed64 result);
// representable is true.
// The oversized product cancels before narrowing, so result is MaxValue.Scalar APIs include fused add/subtract and multiply/divide families. Matching vector, matrix, transform, and geometry methods apply the same principle to a whole operation and fail atomically when a required final output cannot be represented.
A transformed point may leave Q32.32 temporarily, then return after translation or relative-frame cancellation. Full-domain transform methods keep products and translation terms together until final materialization.
Geometry often needs a reliable comparison more than it needs a materialized
distance. Wide predicates can compare squared distances, discriminants,
projections, and candidate features before converting the selected witness to
Fixed64.
That lets the library:
- classify containment, separation, and degeneracy from exact signs;
- rank candidates before a public distance saturates;
- retain rational or rigid-frame points until a world point is requested;
- round a final witness once; and
- build conservative clipped bounds without underestimating the shape.
This helps both extreme coordinates and ordinary near-cancelling expressions.
It does not increase the range of stored Fixed64 state.
The implementation uses signed fixed-width values composed of 64-bit limbs:
| Internal type | Width | Typical role |
|---|---|---|
Signed192 |
192 bits | Endpoint differences, products, and foundational geometry |
Signed320 |
320 bits | Multi-component dot, cross, and determinant expressions |
Signed576 |
576 bits | Finite-axis ratios and roots |
Signed704 |
704 bits | Expanded finite-axis and radical evaluation |
Signed832 |
832 bits | The widest fixed-degree conic comparisons currently required |
These are not user-selectable precision modes. Each algorithm uses a width
proven sufficient for its bounded expression. Fixed-size value types and stack
scratch storage avoid a runtime BigInteger dependency and keep hot paths
allocation-light.
The Wide layer owns representation mechanics—carry, borrow, sign extension, division, roots, comparison, and round-half-to-even narrowing. Public owners still decide whether an operation saturates, clips, throws, or reports failure.
Keeping the layer internal preserves a small public numeric API and prevents limb layouts from becoming serialization contracts. The widths can evolve as new bounded expressions are proven without asking users to choose or persist a wide number format.
Gravitas is the runtime assembly's sole external production friend. It may compose policy-neutral arithmetic behind Gravitas-owned physics semantics, but Wide types do not belong in Gravitas public APIs or host adapters.
- Use ordinary operators when every intermediate is intentionally a standalone
Fixed64value. - Use an existing fused method when the complete expression is the operation you mean.
- Prefer a
Try*form when a documented output failure must be distinct from a valid saturated value. - Use relative coordinates, chunking, or rebasing when final application state itself exceeds Q32.32.
- Do not approximate an extreme deterministic predicate with floating point. Check the API for an existing full-domain operation first.
For ordinary representation and operator behavior, continue with Fixed64 Representation. For the geometry built on these guarantees, see Bounds and Geometry.