2015-10-12 3 views
1

Я пишу эту функцию, которая принимает 2 цифры ref и данные и проверяет, находится ли данные в пределах 5% от ref.Как проверить, находится ли int в пределах +/- некоторый процент

Примера: если порядковый 100 и данные 102, он возвращает 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; 
} 

Проблемы я имею в lower_bound. Когда я передаю 100 как ref, верхний_отчет равен 105, но по какой-то причине lower_bound равен 94, когда он действительно должен быть 95.

+0

«двойной» округляется как «int». –

+0

Возможный дубликат [Почему десятичные числа не могут быть представлены точно в двоичном?] (Http://stackoverflow.com/questions/1089018/why-cant-decimal-numbers-be-represented-exactly-in-binary) –

+0

Возможно, я ошибался в отношении округления: http://ideone.com/vPmQDQ –

ответ

0

0.05 * ref запускает правила продвижения типа C и оценивает до double.

ref - 0.05 * ref затем делает то же самое, поэтому выход (ref - 0.05 * ref) - это double.

Эффект int lower_bound = (ref - 0.05 * ref); должен затем присвоить doubleint, который выполняется путем усечения. Который для положительного числа с плавающей запятой означает округление.

Поэтому все, от чего вы страдаете, является округлой ошибкой. Вы можете использовать round(ref - 0.05 * ref), чтобы получить ближайшее целое число, а не одно под ним, или вы можете выполнить весь расчет в целых числах, например. lower_bound = (ref * 95)/100;

+0

@MooseBoys выглядит так, как будто вы правы ... http://ideone.com/vPmQDQ –

+0

@Ben Хорошо в идеоне он работает, но я понял, что закругление * может быть действительно проблемой. – MooseBoys

+0

Downvote: что я ошибся? Кто-нибудь? – Tommy

0
0.05 * ref 

будет конвертировать результат в два раза. Он не может быть непосредственно представлен как число с плавающей запятой, поэтому фактический результат - это что-то вроде 5.000000001.

100 - 5.000001 = 94.99999999 

который затем усекается до 94.

Это зависит от того, как вы хотите, чтобы решить эту проблему, но вы могли бы, например, умножить на 5, а затем разделить на 100, чтобы получить 5%. Но здесь вам еще нужно определить, как округлить результат после деления.

+0

'5.0' может быть точно выражен как число с плавающей запятой: 0 [.] 101 в мантиссе, значение 3 для экспоненты. – Tommy

2

Чтобы преобразовать это в целочисленной арифметике, мы имеем ref - 0.05 * ref = 0.95 * ref = 19/20 * ref, а так же ref + 0.05 * ref = 21/20 * ref.

Итак, мы хотим, чтобы проверить, является ли 19/20 * refdata ≤ 21/20 * ref, или другими словами, является ли 19 * ref <= 20 * data && 20 * data <= 21 * ref. Затем код становится

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; 
} 

Обратите внимание, что никаких проблем с арифметикой с плавающей запятой нет. Однако у вас могут быть проблемы с переполнением целых чисел, если ref и data слишком велики (т. Е. Положительны) или слишком малы (т. Е. Отрицательны).

+1

Чтобы избежать проблем с диапазоном, вероятно, '20LL * data> = 19LL * ref && 20LL * data <= 21LL * ref' решит его. – chux

0

Просто измените int lower_bound и int upper_bound к float lower_bound и float upper_bound в вашем коде, потому что вы могли бы в конечном итоге с десятичными ответами при расчете lower_bound = (ref - 0.05 * ref) and upper_bound = (ref + 0.05 *ref). Например, если ref=90 будет upper_bound будет 94.5 и lower_bound будет 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; 
} 
1

lower_bound принимает значение 94 в связи с 0.05 не быть точно представим как double и преобразование обратно в int обрежет фракцию.

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; 

Простая альтернатива с использованием только целочисленной математики.

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

Как показывают приведенные выше и различные другие ответы не в состоянии с отрицательной ref или с большими значениями, после более безопасный метод, который я считаю, работает для всех 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); 
} 
Смежные вопросы