4

Is it possible to shift right the digits of an integer? I mean shifting the actual digits of the integer values and not the bits of its binary representation - for example shifting right the number 435 twice would result in 4. I want to know if there is a way to replace the rudimentary method of dividing the number by 10 the number of times that's equal to the number of digits you want to remove.

Thanks

jon Prime
  • 51
  • 1
  • 5
  • 2
    Did you face any issues with the `rudimentary method`? – Sourav Ghosh Mar 20 '15 at 14:29
  • 6
    Dividing with the base of the numeral system (10 in this case) IS the way to do it. Shifting the actual bits around is the same operation as dividing/multiplying by two (the base of the binary system). These two operations are the same basically. This doesn't mean that you can't invent some different workaround, but I doubt that there's a simpler method. – notadam Mar 20 '15 at 14:30
  • `int rshift_digit(int value, int shift) { return value / (10*shift); }` – Brian McFarland Mar 20 '15 at 14:39
  • 3
    @BrianMcFarland Shouldn't it be `pow(10, shift)` (or an other power function) instead? – Juri Robl Mar 20 '15 at 14:45
  • I don't see how the rshift_digit function could work as it uses 10*shift, so for shift==2 it divides by 20 instead of 10^2 = 10*10 = 100. – juhist Mar 20 '15 at 14:45
  • @JuriRobl, yes it should be, thanks! – Brian McFarland Mar 20 '15 at 14:48
  • 2
    Nobody mentioned it here, but I'd also consider working on *binary-coded decimal* representation, i.e. all the digits are stored separatedly, in base 10, in memory. This makes it harder to do mathematical operations on it, but easier to display and do other numerical tricks. – Bregalad Mar 20 '15 at 14:59

2 Answers2

3

If you are looking for speed, then I'd rely on compiler optimisation with the following code:

unsigned long long rshift(unsigned long long x, unsigned n) {
    switch(n){
    case 0: return(x);
    case 1: return(x/10);
    case 2: return(x/100);
    ...
    case 19: return(x/10000000000000000000ULL);
    default: if (x<10000000000000000000ULL) return(0);
             printf("Sorry, not yet implemented\n");
             return(0);
    }
}
Marian
  • 7,402
  • 2
  • 22
  • 34
  • An interestign approach. I wonder if this is faster than my approach. Of course, if you make it "static inline" and the shift amount is constant, then this is probably faster. – juhist Mar 20 '15 at 14:52
  • @Marian As `long long` is only _at least_ 64-bit, a complete solution would need to take into account the compiler's actual `long long` width. – chux - Reinstate Monica Mar 20 '15 at 14:57
  • Why are all the answerers on this post insistent on wrecking their previous support for negative numbers? – Bathsheba Mar 20 '15 at 15:08
2

If you think the rudimentary method is too slow, you can use the exponentiation by squaring algorithm to calculate a divisor: http://en.wikipedia.org/wiki/Exponentiation_by_squaring

Or you can use a lookup table:

int divisors_i[] = {1, 10, 100, 1000, 10*1000, ...}

unsigned long long divisors_ull[] = {1ULL, 10ULL, 100ULL, 1000ULL, 10ULL*1000ULL,
                                     100ULL*1000ULL, 1000ULL*1000ULL,
                                     10ULL*1000ULL*1000ULL, 100ULL*1000ULL*1000ULL, ...}

Be sure to select the appropriate data type for the lookup table. If you have an int, use the int lookup table. If you have an huge number, unsigned long long might be a better type to use.

The fastest way is the use of a lookup table. It is small enough to fit into the L1 cache of any CPU, so it is way faster than exponentiation by squaring.

juhist
  • 4,210
  • 16
  • 33
  • this will shift **left**. OP needs to divide by the multiplier[n] – DrKoch Mar 20 '15 at 14:35
  • Oh, you're right. I changed "multipliers" into "divisors". Now it has the correct name. – juhist Mar 20 '15 at 14:36
  • @juhist.: Use long long instead of int. And you can only have upto 10 number (from 1 to 10^9). After that it will cause overflow. – user2736738 Mar 20 '15 at 14:37
  • Well, I don't think anybody is still using systems where int is 16 bit. But in theory you're right, which is why I changed the example code to use unsigned long long. It's a bit uglier now as the constants have to be in the form 10ULL*1000ULL. I could use 1e1, 1e2, 1e3, ... but I don't particularly like the automatic floating point -> integer conversion. – juhist Mar 20 '15 at 14:43
  • Methinks this last edit has ruined this answer. (But I'll keep my +1 in). You'll now be promoting the argument to an unsigned long long which (i) is expensive, and (ii) will cause errors for negative numbers. – Bathsheba Mar 20 '15 at 14:44
  • 3
    "don't think anybody is still using systems where int is 16 bit." 10s, if not 100s of millions of embedded processors are made each year (2014) with 16-bit `int`. – chux - Reinstate Monica Mar 20 '15 at 14:46