Thread overview
Phobos addition - std.conv.toInt(char[] s, int base)
Probably does not work as intended
May 30, 2006
Bob W
May 30, 2006
I've been missing the ability to convert any base of string numerical literal into an int (i.e. strtol()), so I wrote a new toInt that allows any base from 2 to 36 (bases greater than 10 use a-z, case-insensitive).  It accomplishes this by using a translation table (generated by std.string.maketrans() of course) for easy parsing.  It's written pretty much the same way that the existing toInt is.

int toInt(char[] s, int base)
{
 assert(base >= 2 && base <= 36);

 static char[] transTable =
 [
  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
  16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
  32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
  48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
  64, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
  73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 91, 92, 93, 94, 95,
  96, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
  73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 123, 124, 125, 126,
  127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
  140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152,
  153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165,
  166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178,
  179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
  192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204,
  205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
  218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230,
  231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243,
  244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
 ];

    int length = s.length;

 if(!length)
  throw new ConvError(s);

 int sign = 0;
 int v = 0;

 char maxDigit = '0' + base - 1;

 for(int i = 0; i < length; i++)
 {
  char c = transTable[s[i]];

  if(c >= '0' && c <= maxDigit)
  {
   uint v1 = v;
   v = v * base + (c - '0');

   if(cast(uint)v < v1)
    throw new ConvOverflowError(s);
  }
  else if(c == '-' && i == 0)
  {
   sign = -1;

   if(length == 1)
    throw new ConvError(s);
  }
  else if(c == '+' && i == 0)
  {
   if(length == 1)
    throw new ConvError(s);
  }
  else
   throw new ConvError(s);
 }

 if(sign == -1)
 {
  if(cast(uint)v > 0x80000000)
   throw new ConvOverflowError(s);

  v = -v;
 }
 else
 {
  if(cast(uint)v > 0x7FFFFFFF)
   throw new ConvOverflowError(s);
 }

 return v;
}


May 30, 2006
"Jarrett Billingsley" <kb3ctd2@yahoo.com> wrote in message news:e5gcr4$27r5$1@digitaldaemon.com...
> I've been missing the ability to convert any base of string numerical literal into an int (i.e. strtol()), so I wrote a new toInt that allows any base from 2 to 36 (bases greater than 10 use a-z, case-insensitive). It accomplishes this by using a translation table (generated by std.string.maketrans() of course) for easy parsing.  It's written pretty much the same way that the existing toInt is.

Unless you specifically intend to get identical results
for e. g.   toInt(";:=",16)   and   toInt("BAD",16) ,
you need to exclude a certain character range from
conversion.

Have a look at this one:

------------

int toInt(char[] s, int base) {
  const char XX=0xff;
  const char[256] transTable = [
    XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,
    XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,
    XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,
     0, 1, 2, 3, 4, 5, 6, 7, 8, 9,XX,XX,XX,XX,XX,XX,
    XX,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
    25,26,27,28,29,30,31,32,33,34,35,26,XX,XX,XX,XX,
    XX,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
    25,26,27,28,29,30,31,32,33,34,35,26,XX,XX,XX,XX,
    XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,
    XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,
    XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,
    XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,
    XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,
    XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,
    XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,
    XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX
  ];

  assert (base>0 && base<37);

  int length = s.length;
  if (!length)  throw new ConvError(s);

  uint minus = 0, v = 0;

  foreach (i, c; s) {
    char ct=transTable[c];

    if (ct<base) {
      uint v1 = v;
      v = v*base + ct;
      if (v<v1)  throw new ConvOverflowError(s);
    }

    else if (!i) {
      if (c == '-')       minus=1;
      else if (c != '+')  throw new ConvError(s);
    }

    else  throw new ConvError(s);
  }

  if (v & 0x80000000) {
    if (!minus)          throw new ConvOverflowError(s);
    if (v & 0x7fffffff)  throw new ConvOverflowError(s);
  }

  return cast(int)(minus ? 0-v : v);
}

-----------------

Please note that my sample program accepts a base of 1. This is still a valid base but it probably has no practical merits.


May 30, 2006
"Bob W" <nospam@aol.com> wrote in message news:e5h97s$muj$1@digitaldaemon.com...

> Unless you specifically intend to get identical results
> for e. g.   toInt(";:=",16)   and   toInt("BAD",16) ,
> you need to exclude a certain character range from
> conversion.

Ooh, how insidious.  Thanks for catching that.