Harry Singh
Harry Singh

Reputation: 23

How to check if an int is within +/- some percentage

I'm writing this function that takes in 2 numbers ref and data and checks if data is within 5% of ref.

Example: if ref is 100 and data is 102, it returns 1.

int within_5_percent(int ref, int data)
{
int result = 0;
int lower_bound = (ref - 0.05 * ref);
int upper_bound = (ref + 0.05 * ref);

//  printf("Upper: %d\n",upper_bound);
//  printf("Lower: %d\n", lower_bound);
if(data >= lower_bound && data <= upper_bound)
{
    result = 1;
}
else
{
    result = 0;
}

return result;
}

The problem I'm having is at lower_bound. When I pass 100 as ref, the upper_bound is 105 but for some reason lower_bound is 94 when it should really be 95.

Upvotes: 1

Views: 126

Answers (5)

chux
chux

Reputation: 153498

lower_bound takes on the value of 94 due to 0.05 not being exactly representable as a double and conversion back to int truncates the fraction.

int lower_bound = (int) 100 - (double) 0.05 * (int) 100 -->   
int lower_bound = 100 - 0.05000000000000000277... * 100 -->  
int lower_bound = 94.999....999... -->  
int lower_bound = 94;

A simple alternative using only integer math.

int within5(int ref, int data) {
  int lo = ref - ref/20;
  int hi = ref + ref/20;
  return (lo <= data && data <= hi);
}

As the above and various other answers fail with a negative ref or with large values, following is a more secure method that I believe works for all ref,data.

int within5secure(int ref, int data) {
  int ref20 = abs(ref / 20);
  int lo = ref > INT_MIN + ref20 ? ref - ref20 : INT_MIN;
  int hi = ref < INT_MAX - ref20 ? ref + ref20 : INT_MAX;
  return (lo <= data && data <= hi);
}

Upvotes: 1

Mutex202
Mutex202

Reputation: 19

Just change int lower_bound and int upper_bound to float lower_bound and float upper_bound in your code because you might end up with decimal answers when you calculate lower_bound = (ref - 0.05 * ref) and upper_bound = (ref + 0.05 *ref). For example when ref=90 your upper_bound will be 94.5 and lower_bound will be 85.5.

int within_5_percent(int ref, int data)
{
   int result = 0;
   float lower_bound = (ref - 0.05 * ref);
   float upper_bound = (ref + 0.05 * ref);

   // printf("Upper: %f\n",upper_bound);
   // printf("Lower: %f\n", lower_bound);
 if(data >= lower_bound && data <= upper_bound)
 {
    result = 1;
 }
 else
 {
  result = 0;
 }

 return result;
}

Upvotes: 0

Jack
Jack

Reputation: 116

To convert this into integer arithmetic, we have ref - 0.05 * ref = 0.95 * ref = 19/20 * ref, and similarly ref + 0.05 * ref = 21/20 * ref.

So we want to check whether 19/20 * refdata ≤ 21/20 * ref, or in other words whether 19 * ref <= 20 * data && 20 * data <= 21 * ref. The code then becomes

int within_5_percent(int ref, int data)
{
int result = 0;

//  printf("Upper: %d\n",upper_bound);
//  printf("Lower: %d\n", lower_bound);
if(20 * data >= 19 * ref && 20 * data <= 21 * ref)
{
    result = 1;
}
else
{
    result = 0;
}

return result;
}

Note that any problems with floating-point arithmetic are gone. However, you could have problems with integer overflow if ref and data are too big (i.e. positive) or too small (i.e. negative).

Upvotes: 2

typ1232
typ1232

Reputation: 5607

0.05 * ref

Will convert the result to double. It cannot be directly represended as a floating point number so the actual result is something like 5.000000001.

100 - 5.000001 = 94.99999999

Which is then truncated to 94.

It depends how you want to solve this, but you could for example multiply by 5 and then divide by 100 to get 5%. But here you still have to define how to round the result after the division.

Upvotes: 0

Tommy
Tommy

Reputation: 100632

0.05 * ref triggers C's type promotion rules and evaluates to a double.

ref - 0.05 * ref then does the same thing, so the output of (ref - 0.05 * ref) is a double.

The effect of int lower_bound = (ref - 0.05 * ref); is then to assign a double to an int, which is performed by truncation. Which, for a positive floating point number, means rounding down.

Therefore all you're suffering from is rounding error. You could use round(ref - 0.05 * ref) to get the nearest integer rather than the one below it, or you could perform the whole calculation in integers, e.g. lower_bound = (ref * 95) / 100;

Upvotes: 0

Related Questions