Reputation: 23
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
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
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
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 * ref
≤ data
≤ 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
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
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