1

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.

Lee Taylor
  • 7,761
  • 16
  • 33
  • 49

5 Answers5

2

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).

Jack
  • 116
  • 5
1

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);
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
0

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;

Tommy
  • 99,986
  • 12
  • 185
  • 204
  • @Ben Well in ideone it works, but I realized rounding *could* actually be the problem. – MooseBoys Oct 12 '15 at 16:50
  • Thanks @Tommy I used (lower_bound = (ref * 95) / 100;) and seems to working fine, but I also compiled my original code using a different compiler and it gave correct output, must be something with code blocks. – Harry Singh Oct 12 '15 at 16:58
0
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.

typ1232
  • 5,535
  • 6
  • 35
  • 51
  • `5.0` can be exactly expressed as a floating point number: 0[.]101 in the mantissa, a value of 3 for the exponent. – Tommy Oct 12 '15 at 16:54
0

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;
}
Mutex202
  • 19
  • 2