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?
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.
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.
I believe it's true that the input is acceptable if and only if either:
or
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With