Question

I have a class that internally represents some quantity in fixed point as 32-bit integer with somewhat arbitrary denominator (it is neither power of 2 nor power of 10).

For communicating with other applications the quantity is converted to plain old double on output and back on input. As code inside the class it looks like:

int32_t quantity;
double GetValue() { return double(quantity) / DENOMINATOR; }
void SetValue(double x) { quantity = x * DENOMINATOR; }

Now I need to ensure that if I output some value as double and read it back, I will always get the same value back. I.e. that

x.SetValue(x.GetValue());

will never change x.quantity (x is arbitrary instance of the class containing the above code).

The double representation has more digits of precision, so it should be possible. But it will almost certainly not be the case with the simplistic code above.

  • What rounding do I need to use and
  • How can I find the critical would-be corner cases to test that the rounding is indeed correct?
Était-ce utile?

La solution

Any 32 bits will be represented exactly when you convert to a double, but when you divide then multiply by an arbitrary value you will get a similar value but not exactly the same. You should lose at most one bit per operations, which means your double will be almost the same, prior to casting back to an int. However, since int casts are truncations, you will get the wrong result when very minor errors turn 2.000 into 1.999, thus what you need to do is a simple rounding task prior to casting back.

You can use std::lround() for this if you have C++11, else you can write you own rounding function.

You probably don't care about fairness much here, so the common int(doubleVal+0.5) will work for positives. If as seems likely, you have negatives, try this:

int round(double d) { return d<0?d-0.5:d+0.5; }

Autres conseils

The problem you describe is the same problem which exists with converting between binary and decimal representation just with different bases. At least it exists if you want to have the double representation to be a good approximation of the original value (otherwise you could just multiply the 32 bit value you have with your fixed denominator and store the result in a double).

Assuming you want the double representation be a good approximation of your actual value the conversions are nontrivial! The conversion from your internal representation to double can be done using Dragon4 ("How to print floating point numbers accurately", Steele & White) or Grisu ("How to print floating point numbers quickly and accurately", Loitsch; I'm not sure if this algorithm is independent from the base, though). The reverse can be done using Bellerophon ("How to read floating point numbers accurately", Clinger). These algorithms aren't entirely trivial, though...

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top