Documentation
Namespace
All Generic Arithmetic classes are in the GenericArithmetic namespace. Add this at the top of your scripts to access them:
using GenericArithmetic;
How It Works
Generic Arithmetic lets you write generic classes and methods that perform arithmetic and comparison operations on any numeric or operator-enabled type, without knowing the concrete type at compile time.
The key concept is the computator: a small class that knows how to perform every operation (+, -, *, /, comparisons, etc.) for a specific type combination. Computators are discovered automatically at startup; you never call them directly.
Built-in computators cover all common Unity numeric and vector types:
int, float, double, long,
byte, sbyte, short, uint,
ulong, ushort, char, decimal,
Vector2, and Vector3.
For any other type, you add support in one of three ways covered below.
There are three fallback strategies for types without a registered computator, configured in the Configuration inspector:
- Throw - throws an exception immediately; safest for catching missing computators early.
- Dynamic - uses C#
dynamic; reliable and handled by the .NET runtime, but slower. Does not work on IL2CPP. - Expression - compiles a delegate on first use; near-native speed after the first call, but does not work on IL2CPP.
Computable<T>
Computable<T> is a lightweight struct wrapper around any value. It
exposes all arithmetic, bitwise, comparison, and boolean operators directly on the wrapped
value, so generic code can use standard C# operator syntax.
Operations
Wrap a value with an implicit cast or with Computable<T>.From(), then
use operators naturally:
class MyGenericClass<T>
{
void Example(T a, T b)
{
Computable<T> ca = a;
Computable<T> cb = b;
T sum = ca + cb;
T diff = ca - cb;
T prod = ca * cb;
bool gt = ca > cb;
}
}
The underlying value is always accessible through the value field or by
implicitly casting back to T.
To cast between types, use From<TOther>() and To<TOther>():
Computable<float> cf = Computable<float>.From<int>(42); // casts int to float
int i = cf.To<int>(); // casts float to int
Same-Type Limitation
Operators on Computable<T> only work when both operands share the same
T. Cross-type operations like int + float are not supported
through this struct; use Calculate<TResult>
instead.
// OK: same type on both sides
Computable<int> a = 3;
Computable<int> b = 5;
Computable<int> result = a + b;
// Cross-type: use Calculate instead
float result2 = Calculate<float>.Add<int, float>(3, 1.5f);
Calculate<TResult> & Evaluate
These static classes give you explicit control over operations and their result type. They are the best choice when operands have different types, when you need the result in a specific type, or when you want to keep the code as a plain method call rather than introducing wrapper structs.
Binary Operations
Calculate<TResult> takes two type parameters for the operands and
returns TResult:
// Multiply a float scale factor by a Vector3
Vector3 scaled = Calculate<Vector3>.Multiply<float, Vector3>(scale, direction);
// Add two ints, produce a long
long total = Calculate<long>.Add<int, int>(a, b);
Available binary operations: Add, Subtract, Multiply, Divide, Modulo, And, Or, Xor.
Unary Operations
Unary methods take one type parameter for the operand:
float negated = Calculate<float>.Negate<float>(value);
int shifted = Calculate<int>.LeftShift<int>(value, 2);
long casted = Calculate<long>.Cast<int>(value);
Available unary operations: Plus, Negate, Invert, Complement, Increment, Decrement, Cast, LeftShift, RightShift.
Comparisons
Evaluate handles comparisons and boolean tests; all methods return
bool:
bool isGreater = Evaluate.GreaterThan<float, float>(a, b);
bool isTrue = Evaluate.True<MyType>(myValue);
Available comparisons: Equal, NotEqual, GreaterThan, LessThan, GreaterThanOrEqual, LessThanOrEqual, True, False.
Try Methods
Both classes expose a nested Try class with non-throwing versions of every
method. They return true and set the out parameter on success,
or return false (without throwing) when the operation is not supported for
the given types:
if (Calculate<float>.Try.Add<int, float>(a, b, out float result))
Debug.Log(result);
if (Evaluate.Try.GreaterThan<MyType, MyType>(x, y, out bool gt))
Debug.Log(gt);
Math<T>
Math<T> is a static utility class that provides generic equivalents of
UnityEngine.Mathf and System.Math, built entirely on
Calculate<T> and Evaluate. It works with any numeric
type T that supports the necessary operators.
Typed constants are exposed as static readonly fields, computed once per
closed type (e.g. Math<float>) and cached:
Zero, One, PI, E,
Deg2Rad, Rad2Deg.
float clamped = Math<float>.Clamp(value, 0f, 1f);
float angle = Math<float>.DeltaAngle(current, target);
float s = Math<float>.Sin(Math<float>.PI / 6f);
A nested Math<T>.Try class mirrors every method with a non-throwing
version, following the same pattern as Calculate<T>.Try:
if (Math<float>.Try.Sqrt(value, out float root))
Debug.Log(root);
double internally. All other methods
(Abs, Sign, Min, Max,
Clamp, Lerp, Repeat, etc.) stay entirely
within generic arithmetic. See the Public API for
the full method list.
Source Generator
The [GenerateComputators] assembly-level attribute tells the included Roslyn
source generator to produce all four computator classes at compile time, with no
.cs files written to Assets. Generated computators are fully AOT-safe, making
this the recommended approach for IL2CPP builds.
Place the attribute in any script in your project (typically in a dedicated
Computators.cs file):
// Single-type form: generates BinaryCalculator, UnaryCalculator,
// BinaryEvaluator, and UnaryEvaluator all at once
[assembly: GenerateComputators(typeof(MyValue))]
// Three-type form: generates BinaryCalculator and BinaryEvaluator
// for cross-type operations (e.g. float * MyValue -> MyValue)
[assembly: GenerateComputators(typeof(MyValue), typeof(float), typeof(MyValue))]
The generator inspects the type's operators at compile time and only emits code for
operations that are actually defined; unsupported ones throw
InvalidOperationException.
Assets/Generic Arithmetic/Source Generator/).
Priority order: Manual > Source Generated > Default computator.
Manual Computators
Manual computators are regular C# classes you write yourself. They run at the same speed as source-generated ones, but give you full control over how each operation is implemented, making them the right choice when you need custom behavior (e.g. clamping on add, saturating arithmetic, or any logic that goes beyond a direct operator call).
Derive from one of the four abstract base classes and implement its methods. The package discovers subclasses automatically at startup with no registration step, and they take priority over source-generated and default computators.
Calculate<T> binary
operations, only a BinaryCalculator is needed.
Using the Creator Tool
The easiest way to create a manual computator is through the editor tool at Tools > Generic Arithmetic > Create Manual Computator Script.
Select the output folder in the Project window, enter the type name (or three types for a
cross-type computator), and click Create. The generated script contains
all required method stubs with correct implementations pre-filled where possible.
Only valid operations for the type are inlined; unsupported ones throw
InvalidOperationException.
Writing Manually
Derive from the relevant base class and implement all abstract methods:
// Binary arithmetic for a custom type
class ManualBinaryCalculatorMyValue : BinaryCalculator<MyValue, MyValue, MyValue>
{
public override MyValue Add(MyValue a, MyValue b) => a + b;
public override MyValue Subtract(MyValue a, MyValue b) => a - b;
public override MyValue Multiply(MyValue a, MyValue b) => a * b;
public override MyValue Divide(MyValue a, MyValue b) => a / b;
public override MyValue Modulo(MyValue a, MyValue b) => throw new InvalidOperationException();
public override MyValue And(MyValue a, MyValue b) => throw new InvalidOperationException();
public override MyValue Or(MyValue a, MyValue b) => throw new InvalidOperationException();
public override MyValue Xor(MyValue a, MyValue b) => throw new InvalidOperationException();
}
// Cross-type: float * MyValue -> MyValue
class ManualBinaryCalculatorMyValueCross : BinaryCalculator<MyValue, float, MyValue>
{
public override MyValue Multiply(float a, MyValue b) => b * a;
// ... other methods
}
The four base classes and their purposes:
BinaryCalculator<TResult, TParamA, TParamB>- arithmetic and bitwise operations (+, -, *, /, %, &, |, ^).UnaryCalculator<TResult, TParam>- unary operations (+, -, !, ~, ++, --, <<, >>) and explicit casting.BinaryEvaluator<TParamA, TParamB>- comparison operations (==, !=, >, <, >=, <=).UnaryEvaluator<TParam>- boolean evaluation (operator true/operator false).
Configuration
All editor tools are under Tools > Generic Arithmetic:
Open the configuration asset via Tools > Generic Arithmetic > Configuration to control how the package behaves at runtime.
Default Computator
Controls what happens when an operation is called for a type that has no registered computator.
- Throw - throws an exception; safest for catching missing computators early.
- Dynamic - uses C#
dynamic; reliable and handled by the .NET runtime, but slower. Does not work on IL2CPP. - Expression - compiles a delegate on first use; near-native performance after the first call, but does not work on IL2CPP.
Built-in Computators
A read-only list of all computators shipped with the package (all primitive and vector types). Each entry shows whether it is source-generated or written manually.
User Computators
A list of all computators found in your project assemblies. Click any entry to ping its source script in the Project window. This section is useful for verifying that your computators were picked up correctly.
Assets/Resources/Generic Arithmetic Configuration.asset the first time it
is accessed. You can safely commit it to version control.
Tests
Unit Tests
The package ships with NUnit tests located in
Assets/Generic Arithmetic/Tests/. Run them from the
Window > General > Test Runner panel in Unity. They cover:
- Computable<T> - implicit conversions,
From/Tocasting,Equalsoverride (against anotherComputable<T>, a bareT,null, and an unrelated type),ToString/GetHashCodedelegation, and operator wiring. - Calculate<T>.Try - returns
truewith the correct result for valid operations, returnsfalsewithout throwing for invalid ones, and correctly returnstruewithPositiveInfinityfor float division by zero. - Evaluate.Try - same contract as above for comparison operations.
- Math<T> - constants, basic operations (Abs, Sign, Min, Max, Clamp, Approximately), interpolation (Lerp, MoveTowards, SmoothStep, etc.), periodic (Repeat, PingPong, DeltaAngle), rounding, powers, logarithms, and trigonometry including hyperbolic functions. Each method is tested against known float values.
- Math<T>.Try - returns
trueand the correct result for valid operations, returnsfalsewithout throwing for unsupported types, and correctly usesout bool resultforApproximately.
Bulk Tests
Open Tools > Generic Arithmetic > Bulk Tests to run all registered computators against a broad set of predefined values and review the results visually. This is useful for spotting edge cases across many type combinations at once.
Results are color-coded: green for success, yellow for a wrong value, red for an unexpected exception, and orange when an expected exception was not thrown. Use the Result Type and Operations filter dropdowns to narrow down the view to the cases you care about.
Demo
The package ships with a benchmark scene that compares four approaches to generic
arithmetic: non-generic reference, naive generic, Computable<T>, and
Calculate<T>. Run it to get a sense of the relative performance cost
of each approach on your target platform.
Delete the Demo folder once you are done to remove unused assets from your project.
Author & Contact
Created by Juste Tools.
- Contact: justetools@gmail.com
- Other assets: assetstore.unity.com/publishers/52427
If you enjoy Generic Arithmetic, please consider leaving a review on the Asset Store. Your feedback helps improve the tool!