Why does 1.2 % 0.01 not equal to zero?

  • Thread starter Thread starter Bernard.Mangay
  • Start date Start date
B

Bernard.Mangay

The remainder is non zero due to rounding errors. How can I remove the
rounding errors?
 
The same way you deal with rounding errors with any floating point  
calculation: don't expect exact results.  Instead, any time you want todo  
a comparison, you need to decide how close is "good enough" and  
incorporate that into your comparison.

For example:

     double result = 1.2 % 0.01;

     if (result < 0.000001)
     {
         // might as well be zero!
     }


I thought that might work, but sometimes it gives me a remainder of
0.099999999 which fails the fix you suggested.
 
Bernard.Mangay said:
I thought that might work, but sometimes it gives me a remainder of
0.099999999 which fails the fix you suggested.

Only because it's an error on the other side - easy to fix:

if (result < 0.000001 || result > 0.01-0.000001)
{
// Might as well be zero
}
 
Tom said:
How about forcing integer % implementation?

int mult = 100000;
int remainder = (Int32)(1.2 * mult) % (Int32)(0.01 * mult);

Because it _doesn't solve the problem_.

The binary representation of .01 may be a little more or less than .01.
When you multiply by 100000, the result may be a little more or less than
1000.00. If it is less, when you cast to integer without any rounding, you
will get 999. Same goes for the other operand.

Furthermore, you are still doing more operations than necessary. You've
eliminated the FP division, but you have two FP multiplies. You're better
off (at least in the case of constants) multiplying by the reciprocal,
rounding, subtracting -- cheap FP operations, probably cheaper than integer
modulo -- and performing a equal-with-tolerance test.

I.e.

double x = 1.2;
const double divisor = 0.01;
const double divisor_recip = 1.0 / divisor; // evaluated at compile-time

double divided = x * divisor_recip;
double remainderoverdivisor = divided - Math.Round(divided); // may replace
with Math.Floor(divided + .5) if faster

if (Math.Abs(remainderoverdivisor) < (.0001 * divisor_recip)) ... //
compile-time expression again, introduce another "const" variable as needed
to force compile-time evaluation


Even when divisor is not known at compile-time, this method may still yield
considerable savings if the reciprocal operation can be moved outside a loop
for example.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Back
Top