Personal Agility - Thinking Outside Your Own Box
Essential programmer skill - the ability to approach a problem from more that one direction.
How This Tip Was Born
A homework question was recently posted in the C# forum (the following is a paraphrased version): "Given an integer (assumed to beInt32
) value, extract the individual digits using mathematical functions."
Now, everyone (including me) balked at the idea of doing it this way since converting it to a string object would have been more convenient, and proceeded to provide string-based answers. After all, why not use the tools provided by the framework? After a day or so, the question popped up again (by someone else) in Quick Answers, and I started to view this as a personal challenge to come up with an answer.
My initial solution was to perform math on the original value in such a way as to extract the digits starting at the right side of the number and moving to the left side.
List<int> digits = new List<int>(); int counter = 0; double power = 0; while (value != 0) { counter++; power = Math.Pow(10d, 1); digits.Add(value % (int)power); value = (int)((double)value / power); } digits.Reverse();Fine and dandy, and it worked great. However, as most of us know, requirements for a given task often change just as we deliver our gleaming code jewel that's perfect in every way, and this particular task is no exception. Bill Woodruff suggested the following additional (and more stringent) requirements to create a more interesting problem: 1. The integer value can be in the range
int.MinValue
to int.MaxValue
: you can make no assumptions about the range of those values a priori
2. You cannot use try/catch
or any other "error handling" method to "work around" an overflow error.
3. If the integer value < 0, the first digit must also be negative.
4. Your function :
a. Must take the integer value as its sole parameter
b. Can return your choice of List<int>
or int[]
5. Can have no single loop (of any type) that iterates more times than the maximum power to which 10 can be raised which exceeds the integer value input.
My original code already met requirements #1, #2, #4, and #5, so here's what I came up with:
public static List<int> ExtractDigits(int value) { List<int> digits = new List<int>(); int counter = 0; double power = 0; while (value != 0) { counter++; power = Math.Pow(10d, 1); // if we only want the left-most digit to be negativem, we have to // check the value to see if it's less than -10, and then multiply // it by the appropriately signed value of 1 digits.Add((value % (int)power) * ((value < -10) ? -1 : 1)); value = (int)((double)value / power); } digits.Reverse(); return digits; }I was pretty happy with myself, and then Rod Kemp suggested being able to extract the digits starting with the left-most digit. Back to work!
public static List<int> ExtractDigits2(int value) { bool isNegative = (value < 0); double i = 9; double power = 0; List<int> digits = new List<int>(); while (value != 0) { power = Math.Pow(10d, i); int result = (value - (value % (int)power)); // this time, the negative number is the first one we'll be extracting, // so we have to modify our multiplier code a bit. int addResult = (int)((double)result / power) * ((isNegative && (int)i == 9) ? 1 : -1); digits.Add(addResult); value -= result; i--; } return digits; }Now, some of you will notice (and Bill certainly did) that I assumed that the value being passed in was an
Int32
(what an int
type currently translates to), and the variable i
is set to the power of ten represented by int.MaxValue
. While he is right that it's possible to find the actual maximum power value, I view that as unnecessary code (it involves running through a loop to find the value), and the obvious - and IMHO the best - solution is to create method overloads for each of the integer types. This would save us an admittedly minuscule amount of time by not having to traverse a small loop, but any performance gains we can reasonably accomplish without too severe an impact on the code are positive and worth doing. So changing the types in the method above to Int32
(and providing overloads for the other integer types) would address his "no prior knowledge" requirement.