Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check if a spelled number is in a range in C++

I want to check the (numeric) input against a list of ranges (min,max) while the input is partially typed in; in other words, I need an elegant algorithm to check the prefix of a number against a range (without using regular expressions).

Sample testcases:

 1 is in (  5,   9) -> false  6 is in (  5,   9) -> true  1 is in (  5,  11) -> true  (as 10 and 11 are in the range)  1 is in (  5, 200) -> true  (as e.g. 12 and 135 are in the range) 11 is in (  5,  12) -> true 13 is in (  5,  12) -> false  13 is in (  5,  22) -> true 13 is in (  5, 200) -> true  (as 130 is in the range)  2 is in (100, 300) -> true  (as 200 is in the range) 

Do you have any idea?

like image 982
user1694540 Avatar asked Sep 24 '12 13:09

user1694540


People also ask

How do you check if an element is a number in C?

isdigit() function in C/C++ with Examples The isdigit(c) is a function in C which can be used to check if the passed character is a digit or not. It returns a non-zero value if it's a digit else it returns 0.

How do you check if a number lies in a range in Java?

isValidIntValue(x); it returns true if minValue <= x <= MaxValue - i.e. within the range. it returns false if x < minValue or x > maxValue - i.e. out of range.


2 Answers

I believe it's true that the input is acceptable if and only if either:

  • It is a prefix substring of the lower bound converted to string

or

  • The input followed by any number of additional zeros (possibly none) falls into the range

The first rule is required by e.g. 13 is in range (135, 140). The second rule is required by e.g. 2 is in range (1000, 3000).

The second rule can be efficiently tested by a series of multiplies by 10, until the scaled input exceeds the upper bound.

like image 130
Ben Voigt Avatar answered Oct 05 '22 22:10

Ben Voigt


An iterative formulation:

bool in_range(int input, int min, int max) {   if (input <= 0)     return true;    // FIXME handle negative and zero-prefixed numbers   int multiplier = 1;   while ((input + 1) * multiplier - 1 < min)         // min <= [input]999     multiplier *= 10;    // TODO consider overflow   return input * multiplier <= max;                  //        [input]000 <= max } 

A simpler [edit: and more efficent; see below] method is to use truncating integer division:

bool in_range(int input, int min, int max) {   if (input <= 0)     return true;   while (input < min) {     min /= 10;     max /= 10;   }   return input <= max; } 

Testing and profiling:

#include <iostream> #include <chrono>  bool ecatmur_in_range_mul(int input, int min, int max) {   int multiplier = 1;   while ((input + 1) * multiplier - 1 < min)         // min <= [input]999     multiplier *= 10;    // TODO consider overflow   return input * multiplier <= max;                  //        [input]000 <= max }  bool ecatmur_in_range_div(int input, int min, int max) {   while (input < min) {     min /= 10;     max /= 10;   }   return input <= max; }  bool t12_isInRange(int input, int min, int max) {     int multiplier = 1;     while(input*multiplier <= max)     {         if(input >= min / multiplier) return true;         multiplier *= 10;     }     return false; }  struct algo { bool (*fn)(int, int, int); const char *name; } algos[] = { { ecatmur_in_range_mul, "ecatmur_in_range_mul"}, { ecatmur_in_range_div, "ecatmur_in_range_div"}, { t12_isInRange, "t12_isInRange"}, };  struct test { int input, min, max; bool result; } tests[] = { {  1,   5,   9, false }, {  6,   5,   9, true }, {  1,   5,  11, true }, // as 10 and 11 are in the range {  1,   5, 200, true }, // as e.g. 12 and 135 are in the range { 11,   5,  12, true }, { 13,   5,  12, false }, { 13,   5,  22, true }, { 13,   5, 200, true }, // as 130 is in the range {  2, 100, 300, true }, // as 200 is in the range { 13, 135, 140, true }, // Ben Voigt { 13, 136, 138, true }, // MSalters }; int main() {     for (auto a: algos)         for (auto t: tests)             if (a.fn(t.input, t.min, t.max) != t.result)                 std::cout << a.name << "(" << t.input << ", " << t.min << ", " << t.max << ") != "                     << t.result << "\n";      for (auto a: algos) {         std::chrono::time_point<std::chrono::system_clock> start = std::chrono::system_clock::now();         for (auto t: tests)             for (int i = 1; i < t.max * 2; ++i)                 for (volatile int j = 0; j < 1000; ++j) {                     volatile bool r = a.fn(i, t.min, t.max);                     (void) r;                 }         std::chrono::time_point<std::chrono::system_clock> end = std::chrono::system_clock::now();         std::cout << a.name << ": "             << std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count() << '\n';     } } 

Surprisingly (at least to me) iterated division comes out fastest:

ecatmur_in_range_mul: 17331000 ecatmur_in_range_div: 14711000 t12_isInRange: 15646000 
like image 22
ecatmur Avatar answered Oct 05 '22 21:10

ecatmur