3

We currently have some code to extract digits from an int, but I need to convert this to a platform without snprintf, and I am afraid of a buffer overrun. I have started to write my own portable (and optimized) snprintf but I was told to ask here in case someone had a better idea.

int extract_op(int instruction)
{ 
    char buffer[OP_LEN+1];
    snprintf(buffer, sizeof(buffer), "%0*u", OP_LEN, instruction);
    return (buffer[1] - 48) * 10 + buffer[0] - 48;
}

We are using C strings because Speed is very important.

Ariel Bold
  • 245
  • 2
  • 8
  • 4
    You can use the '0' character instead of 48 as a constant: `return (buffer[1] - '0') * 10 + buffer[0] - '0';` That won't solve your problem but things seem more obvious that way. – zneak Aug 31 '10 at 04:17
  • 2
    It seems like you should be able to do this with division by 10 (without snprintf). What I find odd is that `buffer[1]` is apparently more significant than `buffer[0]` – Matthew Flaschen Aug 31 '10 at 04:22
  • Actually, C's string handling is usually a performance detriment to it -- even Visual Basic can beat C for string intensive operations, because getting the length of the string is linear in C and constant about everywhere else. – Billy ONeal Aug 31 '10 at 04:24
  • @Billy Matthew this is an x86 program but it's processing data from a sparc Perl process – Ariel Bold Aug 31 '10 at 04:25
  • 2
    @Billy, that's misleading. Any properly written C code will only compute the length once and reuse it as needed. – R.. GitHub STOP HELPING ICE Aug 31 '10 at 04:30
  • You can also see the answers to this question: http://stackoverflow.com/questions/1397737/how-to-get-the-digits-of-a-number-without-converting-it-to-a-string-char-array – Naveen Aug 31 '10 at 04:42
  • @R.. : Yes, that is true. However, most C code I've seen is not "properly written" by that definition. (To be fair, most of the C code I see is from students) – Billy ONeal Aug 31 '10 at 04:45
  • Can't this "instruction encoding" be changed to use proper bit fields instead of BCD? It will not only be much faster but also easier to process and quite possibly take less space as well. If you can't change the encoding, then use `itoa` instead of `sprintf` if speed is important (Yes, you'll have to test the magnitude of the input because `itoa` doesn't output a fixed-width string. It'll still be faster.) – Ben Voigt Aug 31 '10 at 05:08
  • Rather than telling us how you are doing it now (which seems like an exceedingly slow method). Tells us what you want the code to do (Currently it is imposable to tell without knowing OP_LEN (What happens if OP_LEN is 1?)) Some example inputs and outputs may be useful. – Martin York Aug 31 '10 at 08:24
  • @LokiAstari it's very fast on x86 and written in C/C++ – Ariel Bold Mar 19 '13 at 02:48

5 Answers5

7

You don't need to form instruction into a character array for this purpose; you just need to keep "the two top digits", as follows:

int extract_op(unsigned int instruction)
{
    int first = 0;
    int second = 0;
    while(instruction) {
        second = first;
        first = instruction % 10;
        instruction /= 10;
    }
    return first + 10 * second;
}

I think the expression in the return is wrong, but it does mimic what you're doing: ten times the second digit, plus the first one.

I suspect that speed might be even better than what you're getting now, but that's up to you to measure on your specific platform and compiler, of course.

Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • 3
    Excuse me, but why did you strip all vowels from your locals? (Also, check your loop, you use `first` instead of `frst` right now.) – zneak Aug 31 '10 at 04:25
  • @zneak, no real reason, let me edit it to make it sensible again;-). – Alex Martelli Aug 31 '10 at 04:28
  • @Ari you might also get improvement by using divmod or equivalent function to compute an update your values – Anycorn Aug 31 '10 at 04:30
  • 2
    Alex has the right answer. Any time you're tempted to use strings to do mathematics, step away from the keyboard until the temptation passes. ASCII is not an acceptable form of arithmetic. – Kris Jenkins Nov 27 '10 at 23:27
2

Using sprintf should be fine. sizeof type * 3 * CHAR_BIT / 8 + 2 is a sufficiently large buffer for printing an integer of type type. You can simplify this expression if you assume CHAR_BIT is 8 or if you only care about unsigned formats. The basic idea behind it is that each byte contributes at most 3 digits in decimal (or octal), and you need space for a sign and null termination.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • 3
    `((CHAR_BIT * sizeof(type) - 1) / 3 + 2)` will give a slightly smaller but still safe figure. – caf Aug 31 '10 at 04:44
  • Good point. All my code assumes `CHAR_BIT==8` so I typically just use `sizeof(type)*3+2`, which ends up being less cluttered, but if you want to support odd char sizes, your version is less cluttered and a tighter bound. – R.. GitHub STOP HELPING ICE Aug 31 '10 at 23:57
1

So far there's one answer that swaps the last two digits and one that swaps the first two… it looks to me like "%0*u", OP_LEN is forcing the output to a particular width, and the significance of the extracted digits is predetermined by OP_LEN.

Assuming OP_LEN is a macro, we can get 10^(OP_LEN-2) with

#define DIVISOR ( (int) ( 1.e ## OP_LEN * 0.01 ) )

Then, similar to @zneak's answer,

int extract_op( int instruction )
{
    instruction /= DIVISOR;
    int tens = (instruction / 10) % 10;
    int units = instruction % 10;
    return units * 10 + tens;
}

#undef DIVISOR
Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
0

U can store the digit u are getting into array. THIS ONE WAS THE CODE EXPLAINED BY ALEX. here i am adding some variables.

int a[5];

int extract_op(unsigned int instruction)
{
int i=0;    
int first = 0;
    int second = 0;
    while(instruction) {
        second = first;
        first = instruction % 10;
        instruction /= 10;
    }
    a[i]=first;
}

This is something will work for all integers will have max 5 digits. But still if u want to take dynamic array then u can use link list

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
Rakesh Gondaliya
  • 1,050
  • 3
  • 25
  • 42
0

should work also for 0 and <0.

int extract_op( int instruction )
{
  int numd = 1;
  while( instruction /= 10 )
    ++numd;
  return numd;
}
user411313
  • 3,930
  • 19
  • 16