1

I have written a program that works with IPv6 addresses, and I needed code that converts a four-integer array to decimal string that represented number of IPv6 addresses[1]. Now I ran into the situation when I need to do reverse: convert possibly large number (that do not fits into uint32_t or uint64_t, so atol() or strtol() are helpless here) represented as a single string to four-integer or byte (uint8_t) array (I converted function from [1] from 4-int to variable-uint8_t).

Is there any way to do that?

[1] How to convert a 128-bit integer to a decimal ascii string in C?

Thanks. This is my first question here, so sorry for bad English if any.

Community
  • 1
  • 1
  • 5
    For conversion of ip-addresses to strings and the other way around, please see [`inet_ntop`](http://linux.die.net/man/3/inet_ntop) and [`inet_pton`](http://linux.die.net/man/3/inet_pton). – Some programmer dude Oct 08 '12 at 17:35
  • Hm, no, inet_ntop() does not apply here too; I have on input a decimal number represented as string, for example "188647402" or "65536", and I need to do conversion like strtol() does, but with numbers large than uint32_t or uint64_t can store (so I need either an array of ints to store value into, like in [1], or array of uint8_t like inet_pton() does). Simply, I need [1], but reverse operation. –  Oct 08 '12 at 18:07
  • May I then suggest the [The GNU Multiple Precision Arithmetic Library](http://gmplib.org)? – Some programmer dude Oct 09 '12 at 05:22

1 Answers1

0

This is the kind of situation when using a library is recommended.

Using GMP, you can do these conversions (gmp.c):

#include <stdio.h>
#include <stdlib.h>
#include <gmp.h>

int main(int argc, char *argv)
{
  // From string
  char *s ="199999999999999999999999999999999999999";
  mpz_t i;
  // To big number, using GMP
  // 10 here means base 10.
  mpz_init_set_str(i, s, 10);
  // Just to test if s is 128 bits long
  printf("%s can be represented with %d digits in base 2\n",s, mpz_sizeinbase(i, 2));
  // Then you can print it in hex
  gmp_printf("%#32ZX\n", i);
  // Or convert it back to int[4]
  unsigned int a[4];
  mpz_export(&a, NULL, 1, 4, 0, 0, i);
  for(int x=0;x<4;x++)
      printf("%X\n", a[x]); 
  mpz_clear(i);
  return 0;
} 

Output:

199999999999999999999999999999999999999 can be represented with 128 digits in base 2
0X96769950B50D88F41314447FFFFFFFFF
96769950
B50D88F4
1314447F
FFFFFFFF

I tested this code on a 32 bit Linux system. Please pay attention to different int sizes and endianess on different platforms. You probably want the result in big endian, if so, just change mz_export to:

mpz_export(&a, NULL, 1, 4, 1, 0, i);

To compile the example, don't forget to install gmp and to add -lgmp -std=c99 on gcc command line parameters.

On Ubuntu, you can install gmp with:

sudo apt-get install libgmp-dev

And compile the example, gmp.c:

gcc gmp.c -o gmp -lgmp -std=c99

This code can be a good start for your conversions. You can also initialize i to be a fixed 128 bit number (using init functions of GMP), just in case your big number starts with zeros.

nmenezes
  • 910
  • 6
  • 12
  • Perhaps I have no other choices, but I still think that gmp dependency for a little program is kind of overkill. But thanks for your detailed explanation! –  Oct 09 '12 at 19:21
  • Maybe the best is avoid this very long decimal number at all. If you change the specs to accept an hexadecimal number, then you will only need to work with it as a string and the conversion becomes a trivial matter. Anyway, who generates this very long decimal number? – nmenezes Oct 10 '12 at 07:06
  • I tried to output subnets of ip(v6) addresses within certain range of addresses specified on commandline as address+n, where n is that number. I better will strip such functionality from the program at all or will implement it as you suggested - passing a hexadecimal number instead of decimal one. –  Oct 10 '12 at 16:53