Every Vector/Matrix stores elements at one of four precisions selected by a single TMtxFloatPrecision enum value. The enum combines both real/complex and single/double distinctions in one parameter:
type TMtxFloatPrecision = (mvSingle, mvDouble, mvSingleComplex, mvDoubleComplex);
| Value | Element type | Bytes/element | Real/Complex |
|---|---|---|---|
mvSingle | Single | 4 | real |
mvDouble | Double | 8 | real (default) |
mvSingleComplex | TSCplx (2 × Single) | 8 | complex |
mvDoubleComplex | TCplx (2 × Double) | 16 | complex |
Primary API — set precision when sizing:
The Size overload that takes a TMtxFloatPrecision is the canonical way to pick precision. One call, one argument, no ambiguity. The precision is the last parameter of Size on every container type:
// Vector / TVec
a.Size(1000, TMtxFloatPrecision.mvDouble);
a.Size(1000, TMtxFloatPrecision.mvSingle);
a.Size(1000, TMtxFloatPrecision.mvDoubleComplex);
a.Size(1000, TMtxFloatPrecision.mvSingleComplex);
// Matrix / TMtx
m.Size(10, 20, TMtxFloatPrecision.mvDouble);
m.Size(10, 20, TMtxFloatPrecision.mvSingleComplex);
There is also a Size(Src: TMtxVecBase, aFloatPrecision: TMtxFloatPrecision) overload that sizes the destination to match a source container but overrides its precision — useful when you need a same-shape result in a different precision without copying the data.
Alternative — boolean overloads:
Older code often uses Size overloads that take separate AComplex / aIsDouble booleans instead of a single enum value. These are fully supported and map 1:1 to the TMtxFloatPrecision values:
| Boolean form | Equivalent TMtxFloatPrecision |
|---|---|
Size(N, False, True) or Size(N, False) | mvDouble (default) |
Size(N, False, False) | mvSingle |
Size(N, True, True) or Size(N, True) | mvDoubleComplex |
Size(N, True, False) | mvSingleComplex |
a.Size(100);
a.Size(100, false, true);
a.Size(100, false, false);
a.Size(100, true, true);
a.Size(100, true, false);
m.Size(10, 20);
m.Size(10, 20, false, false);
m.Size(10, 20, true, true);
Prefer the TMtxFloatPrecision form in new code — it collapses the two booleans into a single self-documenting enum value — but the boolean form remains the idiomatic choice when precision is derived from runtime flags you already carry around.
Performance impact:
- Single precision uses half the memory → better cache utilisation
- SSE/AVX processes 2× as many single-precision elements per instruction
- Typical speedup: 1.5-2× for compute-bound operations
- Trade-off: ~7 significant digits (single) vs ~15 (double)
Mixing precisions: There are no implicit precision conversions. All operands of an operation must have the same FloatPrecision; when they do not match, the operation raises EMtxVecInvalidArgument instead of converting silently.
This is deliberate: a precision conversion is expensive, and performing one implicitly inside an ordinary arithmetic call would hide that cost. Convert explicitly when you actually need it, so the expense stays visible in your code.
How to convert explicitly — use CopyTo, either naming the target precision or copying into a destination that already has it:
// src holds single precision data, dst is to become double precision
src.CopyTo(dst, TMtxFloatPrecision.mvDouble); // convert to the given precision
// or: set the destination precision first, then copy into it
dst.FloatPrecision = TMtxFloatPrecision.mvDouble;
src.CopyTo(dst); // converts to Dst.FloatPrecision
TMtxFloatPrecision values are mvSingle, mvDouble, mvSingleComplex, and mvDoubleComplex. To convert into an integer container, use the CopyTo(Dst, Rounding) overload — its TRounding parameter states how the floating-point values are rounded.
Assigning a size or precision property does not convert — it re-allocates. Size(...) and every property that aliases it discard whatever the container held:
| Assigned property | Effect on existing content |
|---|---|
Length, Rows, Cols (size aliases) | re-allocates — content lost |
FloatPrecision, IsDouble (precision aliases) | re-allocates — content lost |
Complex | preserved — nothing is converted |
Complex is the exception precisely because toggling it is not a conversion. It switches between the real and complex form of the same element format (mvSingle ↔ mvSingleComplex, mvDouble ↔ mvDoubleComplex): every element keeps its floating-point format, only the real/complex interpretation of the storage changes. There is nothing to convert, so the values survive — and so does the allocated memory: no re-allocation takes place.
In .NET the data may additionally be moved inside the block already allocated, to satisfy .NET's array alignment rules. Nothing is converted and no new memory is allocated.
The precision properties are the opposite case. Going from single to double (or back) means every single element genuinely has to be converted — the expensive operation MtxVec refuses to perform behind your back. Rather than hide that cost, assigning the property re-allocates and drops the content, leaving CopyTo as the one explicit way to convert. Those properties select the storage format for data you are about to write — which is why assigning dst.FloatPrecision above is safe, since CopyTo overwrites dst anyway.
- To change precision and keep the values →
CopyTo(above). - To change size and keep the values →
Resize, which resizes while preserving the values already allocated.
Checking current precision:
switch (a.FloatPrecision) {
case TMtxFloatPrecision.mvDouble: ... break;
case TMtxFloatPrecision.mvSingle: ... break;
case TMtxFloatPrecision.mvDoubleComplex: ... break;
case TMtxFloatPrecision.mvSingleComplex: ... break;
}
if (a.IsDouble) { ... } // mvDouble or mvDoubleComplex
if (a.IsSingle) { ... } // mvSingle or mvSingleComplex
if (a.Complex) { ... } // mvSingleComplex or mvDoubleComplex