8

I need to convert normalized integer values to and from real floating-point values. For instance, for int16_t, a value of 1.0 is represented by 32767 and -1.0 is represented by -32768. Although it's a bit tedious to do this for each integer type, both signed and unsigned, it's still easy enough to write by hand.

However, I want to use standard methods whenever possible rather than going off and reinventing the wheel, so what I'm looking for is something like a standard C or C++ header, a Boost library, or some other small, portable, easily-incorporated source that already performs these conversions.

jww
  • 97,681
  • 90
  • 411
  • 885
IntellectualKitty
  • 523
  • 1
  • 6
  • 12
  • 1
    I'm not sure a library is needed... `double convert(int16_t x) {return x < 0 ? x/double(32768) : x/double(32767);}`. It can easily be templated and generalized to all int types using `std::numeric_limits`. Anyway it's very short to make and surely not in the std library. Look also here: http://stackoverflow.com/questions/929103/convert-a-number-range-to-another-range-maintaining-ratio – coyotte508 Jun 02 '16 at 00:29
  • 1
    @coyotte508 Although I haven't found it yet, I was really looking/hoping for something already templated, like std::numeric_limits in the standard library, that already does this since I know I'm not the only one to need this. I spent so much of my career writing code that could and should have already been standardized only to find out later that someone _already had_ written that these days I try to find existing, standardized code before writing it myself. BTW, thanks for the link. – IntellectualKitty Jun 02 '16 at 00:51
  • 1
    I would normally agree but when it's one line, it is clearer to have it "inline" (on site) than wrapped in an obscure wannabe-standard name function call. by the way, orthogonally to the question, are you sure you want to use floats ? the precision vary enormously in the range between 0 and 1. – v.oddou Jun 02 '16 at 01:30
  • 1
    @v.oddou That is a good point, and even with a simple problem like this, there can be issues depending on your usage case, e.g., http://stackoverflow.com/questions/599976/convert-quantize-float-range-to-integer-range?rq=1. BTW, I am using doubles (or float64_t as it appears in my code), but I wanted to ask the question generically so that I wouldn't limit the answers I got. I've come across so many good but obscure links on SO, and even if it's not exactly what you're looking (e.g., it uses floats instead of doubles), you can still often find some really good things out there. – IntellectualKitty Jun 02 '16 at 03:44
  • 1
    This problem is incompletely stated. If −32768 and 32767 represent −1 and 1, then either the scaling is different for positive and negative numbers or 0 does not represent 0. In the former case, addition and subtraction between representatives of numbers of opposite sign will give wrong results. In the latter case, simple multiplication of representatives will give wrong results. – Eric Postpischil Feb 11 '20 at 20:05

3 Answers3

9

Here's a templated solution using std::numeric_limits:

#include <cstdint>
#include <limits>

template <typename T>
constexpr double normalize (T value) {
  return value < 0
    ? -static_cast<double>(value) / std::numeric_limits<T>::min()
    :  static_cast<double>(value) / std::numeric_limits<T>::max()
    ;
}

int main () {
  // Test cases evaluated at compile time.
  static_assert(normalize(int16_t(32767)) == 1, "");
  static_assert(normalize(int16_t(0)) == 0, "");
  static_assert(normalize(int16_t(-32768)) == -1, "");
  static_assert(normalize(int16_t(-16384)) == -0.5, "");
  static_assert(normalize(uint16_t(65535)) == 1, "");
  static_assert(normalize(uint16_t(0)) == 0, "");
}

This handles both signed and unsigned integers, and 0 does normalize to 0.

View Successful Compilation Result

Binary Birch Tree
  • 15,140
  • 1
  • 17
  • 13
  • 1
    This is very nice code, and good usage case testing. Thank you. – IntellectualKitty Jun 02 '16 at 03:54
  • 2
    Converting from integer to double is easy enough, but it would be nice to also see a correct solution to go from double to integer (i.e. where `0.99999999999 == 32767` rather than `0.99999999999 == 32766` as is the case when using a similar approach in this direction of `static_cast(value * std::numeric_limits::max()` due to the way static_cast truncates rather than rounds. – Tim MB Aug 16 '16 at 16:14
0

I'd question whether your intent is correct here (or indeed that of most of the answers).

Since you're likely just dealing with something like an integer representation of a "real" value such as that produced by an ADC - I'd argue that in fact a floating point fraction of +32767/32768 (not +1.0) is represented by the integer +32767, as a value of +1.0 can't actually be expressed in this form due to the 2's complement arithmetic used.

PaulArmitt
  • 21
  • 2
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Oct 26 '21 at 19:28
-2

Although it's a bit tedious to do this for each integer type, both signed and unsigned, it's still easy enough to write by hand.

You certainly don't need to do this for each integer type! Use <limits> instead.

template<class T> double AsDouble(const T x) {
    const double valMin = std::numeric_limits<T>::min();
    const double valMax = std::numeric_limits<T>::max();
    return 2 * (x - valMin) / (valMax - valMin) - 1; // note: 0 does not become 0.
}
user31264
  • 6,557
  • 3
  • 26
  • 40