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, mvDouble); // 1000 doubles, real (same as `a.Size(1000)`)
a.Size(1000, mvSingle); // 1000 singles, real
a.Size(1000, mvDoubleComplex); // 1000 complex doubles
a.Size(1000, mvSingleComplex); // 1000 complex singles
// Matrix / TMtx
m.Size(10, 20, mvDouble); // 10×20 doubles (default)
m.Size(10, 20, mvSingleComplex); // 10×20 complex singles
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 |
// TVec / Vector — Size(ALength, AComplex[, aIsDouble])
a.Size(100); // 100 doubles, real (default)
a.Size(100, False, True); // 100 doubles, real (explicit)
a.Size(100, False, False); // 100 singles, real
a.Size(100, True, True); // 100 complex doubles
a.Size(100, True, False); // 100 complex singles
// TMtx / Matrix — Size(ARows, ACols, AComplex[, aIsDouble])
m.Size(10, 20); // 10×20 doubles
m.Size(10, 20, False, False); // 10×20 singles
m.Size(10, 20, True, True); // 10×20 complex doubles
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, mvDouble); // convert to the given precision
// or: set the destination precision first, then copy into it
dst.FloatPrecision := 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 Delphi the switch is nothing more than a flag change. The stored data is not touched at all — not converted, not even moved.
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.
// WRONG — a is re-allocated, its data is gone, nothing was converted
a.FloatPrecision := mvDouble;
// CORRECT — convert a's values into b at the new precision
a.CopyTo(b, mvDouble);
Checking current precision:
case a.FloatPrecision of
mvDouble: ...;
mvSingle: ...;
mvDoubleComplex: ...;
mvSingleComplex: ...;
end;
if a.IsDouble then ... // convenience: mvDouble or mvDoubleComplex
if a.IsSingle then ... // convenience: mvSingle or mvSingleComplex
if a.Complex then ... // true for mvSingleComplex or mvDoubleComplex