I need to convert a set of numbers from one range to another, while maintaining a relative distribution of values.
For example, a vector containing randomly generated floats can be scaled to fit into possible unsigned char values ββ(0..255). Ignoring type conversion, this would mean that no matter what input was provided (e.g. -1.0 to 1.0), all numbers will be scaled to 0.0 to 255.0 (or so).
I created a template class to perform this conversion, which can be applied to the collection using std::transform :
template <class TYPE> class scale_value { const TYPE fmin, tmin, ratio; public: TYPE operator()(const TYPE& v) { TYPE vv(v); vv += (TYPE(0) - fmin);
However, the above code only works correctly for real numbers ( float , double , ...). Integers work when scaling, but even then only whole relations:
float scale_test_float[] = {0.0, 0.5, 1.0, 1.5, 2.0}; int scale_test_int[] = {0, 5, 10, 15, 20};
My current solution to this problem is to save all the internal scale_value value as double and use type conversion as needed:
TYPE operator()(const TYPE& v) { double vv(static_cast<double>(v)); vv += (0.0 - fmin);
This works in most cases, although with some integer errors, as the values ββare truncated rather than rounded. For example, scaling {0,5,10,15,20} from 20..35 to 20..35 , and then back gives {0,4,9,14,20} .
So my question is: is there a better way to do this? In the case of scaling the float s collection, float conversions seem unnecessary, while truncating int errors appear.
As an aside, I was surprised not to notice something (at least nothing obvious) in boost for this purpose. Perhaps I skipped this - different math libraries confuse me.
Edit: I understand that I could specialize operator() for certain types, however, that would mean a lot of code duplication, which defeats one of the useful parts of templates. If there is no way, for example, to specialize once for all non-floating types (short, int, uint, ...).