Scrutinising the bounds of numeric precision.
As it turns out, most cases work as expected,
including the massive errors incurred by input-values given as float.
However: a logarithmic combination integral -> integral runs in loss of precision
So the selection logic for the intermediary working type is still overly simplistic!
The control logic for the various combinations of linear and logarithmic scales is nasty,
and the added path coverage indeed unveiled several bugs.
One of these issues, an unjustified limitation on the input value type of `asLogarithm`,
convinced me to refactor the conversion functions into free functions,
since they effectively ''always span the relation of two scales'' --
writing them as member function introduces an unnecessary asymetry.
There are some corner cases where the C++ number promotion rules can hurt us badly.
Devised a type selection logic to pick a suitable common numeric type explicitly,
using `std::common_type<T1,T2>` in most cases, yet handling explicitly the
case when the computation would wrap into unsigned; in such a mixed sign integral case,
we either use int64_t (when its precision suffices), or long double,
which can hold 64bit of mantissa without precision loss.
LIN -> LIN yet either additive or multiplicative
LOG -> LIN
LIN -> LOG
and different kinds of logarithms
all computations using a common intermediary numeric type,
preferably a suitably sized floating-point
The `join()`-operation allows to combine a value pulled from automation
to be combined with a local ''base value'' stored in the parameter.
When both the source and the target parameter use the same Scale,
this operation is simple; it becomes more tricky once they don't --
because then linear / logarithmic and type conversions must be handled.
I now decided to provide a generic implementation in the Scale (descriptor),
while the enclosing parameter shall handle the dispatch when connecting
to a opaque source with another Scale. And this generic implementation
relies on the same technique as was used for the parameter type conversions,
thus it should be factored out into a library header.
Scales are defined as descriptor records and will be used as const.
Besides some information functions, a Scale
* allows to conform a value to be compliant with the Scale's constraints
* can ''join'' and thus apply a ''feed'' on top of a ''base value'';
this is the foundation for applying an automation function, while
handling multiplicative and logarithmic cases properly