/*
basen.c - Convert numbers between bases.
Jason Hood, 18 & 19 October, 2003 (originally 8 April, 1997).
Public Domain.
*/
#define PVERS "1.00"
#define PDATE "19 October, 2003"
#include
#include
#include
#include
#include
#ifdef __DJGPP__
void __crt0_load_environment_file( char* dummy ) { }
char** __crt0_glob_function( char* dummy ) { return 0; }
#endif
#define DIGITS (sizeof(unsigned long) * CHAR_BIT)
#define MAXBASE 36 // 0..9, A..Z
char point = '.'; // The separator between integer and fraction
int places = 9;
int fraction; // A vulgar fraction was given
unsigned long whole, numer, denom; // The number to convert
int strtonum( const char* str, int base );
char* numtostr( char* str, int base );
char* ultostr( unsigned long num, char* str, int base );
void printnum( int base, int frac );
void help( void )
{
printf(
"BaseN by Jason Hood .\n"
"Version "PVERS" ("PDATE"). Public Domain.\n"
"http://misc.adoxa.cjb.net/\n"
"\n"
"Convert numbers in any base (2-%d) to any other base(s).\n"
"\n"
"basen [*places] [#base] number... [=base[-base]]...\n"
"\n"
" *places Maximum number of digits after the point (default %d)\n"
" #base Input base (default 10)\n"
" number Number(s) in the input base\n"
" =base Output base(s) (default 10)\n"
"\n"
"Numbers can contain points and fractions, but are limited to 32 bits.\n"
"If a minus sign is present, it is ignored.\n"
"Eg: 3.333333 3,333333 3-1/3 3+1/3 \"3 1/3\"\n"
, MAXBASE, places );
exit( 0 );
}
void check_base( int base )
{
if (base < 2 || base > MAXBASE)
{
fprintf( stderr, "Invalid base: %d\n", base );
exit( 1 );
}
}
int main( int argc, char* argv[] )
{
int inpos, outpos, lastnum;
int inbase, outbase[MAXBASE - 1];
int bases;
char* bp;
int j, k;
if (argc < 2 || strcmp( argv[1], "/?" ) == 0
|| strcmp( argv[1], "-?" ) == 0
|| strcmp( argv[1], "--help" ) == 0)
{
help();
}
if (argv[1][0] == '*')
{
places = atoi( argv[1] + 1 );
if (places < 1 || places >= DIGITS)
{
fprintf( stderr, "Places must be between 1 and %d: %s\n",
(int)DIGITS - 1, argv[1] );
return 1;
}
inpos = 2;
}
else
inpos = 1;
while (inpos < argc)
{
inbase = 10;
bases = 0;
for (lastnum = inpos; lastnum < argc; ++lastnum)
{
if (argv[lastnum][0] == '=')
break;
}
outpos = lastnum;
while (outpos < argc && argv[outpos][0] == '=')
{
if (bases == MAXBASE - 1)
{
bases = MAXBASE;
break;
}
j = (int)strtol( argv[outpos] + 1, &bp, 10 );
check_base( j );
outbase[bases++] = j;
if (*bp == '-')
{
k = atoi( bp + 1 );
check_base( k );
if (bases + abs( k - j ) >= MAXBASE)
{
bases = MAXBASE;
break;
}
if (j < k)
{
while (++j <= k)
outbase[bases++] = j;
}
else
{
while (--j >= k)
outbase[bases++] = j;
}
}
++outpos;
}
if (bases == MAXBASE)
{
fputs( "Too many output bases.\n", stderr );
return 1;
}
else if (bases == 0)
outbase[bases++] = 10;
for (j = inpos; j < lastnum; ++j)
{
if (argv[j][0] == '#')
{
inbase = atoi( argv[j++] + 1 );
check_base( inbase );
if (j == lastnum)
break;
}
if (!strtonum( argv[j], inbase ))
{
fprintf( stderr, "%s: %s\n",
(errno == ERANGE) ? "Number out of range"
: "Invalid number", argv[j] );
}
else
{
for (k = 0; k < bases; ++k)
{
if (inbase == outbase[k] && bases == 1)
{
if (fraction)
printnum( inbase, 1 );
}
else
{
printnum( inbase, 0 );
fputs( " = ", stdout );
printnum( outbase[k], 0 );
putchar( '\n' );
}
}
}
}
inpos = outpos;
}
return 0;
}
void printnum( int base, int frac )
{
char sbuf[DIGITS + 1 + DIGITS-1 + 1];
char fbuf[2][DIGITS + 1];
char* str;
str = numtostr( sbuf, base );
if (fraction)
{
if (whole)
printf( "%s ", ultostr( whole, fbuf[0], base ) );
printf( "%s/%s ", ultostr( numer, fbuf[0], base ),
ultostr( denom, fbuf[1], base ) );
if (frac)
printf( "= %s (base %d)\n", str, base );
else
printf( "(%s) base %d", str, base );
}
else
printf( "%s base %d", str, base );
}
// Convert the number in str according to base.
// Returns 1 if successful, 0 otherwise (errno == ERANGE if too big).
int strtonum( const char* str, int base )
{
unsigned long limit;
char* bp;
if (*str == '-')
++str;
fraction = 0;
numer = 0;
denom = 1;
limit = ULONG_MAX / base;
errno = 0;
whole = strtoul( str, &bp, base );
if (*bp == '.' || *bp == ',')
{
point = *bp;
str = bp + 1;
numer = strtoul( str, &bp, base );
if (*bp == '\0')
{
denom = base;
while (++str < bp)
{
if (denom >= limit)
{
errno = ERANGE;
break;
}
denom *= base;
}
}
}
else if (*bp == '-' || *bp == '+' || *bp == ' ')
{
numer = strtoul( bp + 1, &bp, base );
if (*bp == '/')
{
denom = strtoul( bp + 1, &bp, base );
fraction = 1;
}
}
else if (*bp == '/')
{
fraction = 1;
numer = whole;
denom = strtoul( bp + 1, &bp, base );
whole = 0;
}
return (*bp == '\0' && errno != ERANGE);
}
// Convert num to a string, using base. The number is placed at the END of str,
// which should be at least DIGITS+1 bytes.
// Returns the start of the number.
char* ultostr( unsigned long num, char* str, int base )
{
int digit;
str += DIGITS;
*str = '\0';
do
{
digit = (int)(num % base);
num /= base;
if (digit > 9)
digit += 'A' - 10;
else
digit += '0';
*--str = digit;
} while (num != 0);
return str;
}
// Convert the number to a string, using base. The point is placed in the
// middle of str, which should be at least DIGITS*2+1 bytes.
// Returns the start of the number.
char* numtostr( char* str, int base )
{
unsigned long w, n;
int digit, cnt;
char* start;
w = whole;
n = numer;
if (numer >= denom)
{
do
{
++w;
n -= denom;
} while (n > denom);
}
start = ultostr( w, str, base );
if (n != 0)
{
unsigned long d, limit;
d = denom;
limit = ULONG_MAX / base;
while (n >= limit) // Not worried about overflow here
{
n >>= 1;
d >>= 1;
}
cnt = 0;
str += DIGITS;
*str++ = point;
do
{
n *= base;
digit = (int)(n / d);
n %= d;
if (digit > 9)
digit += 'A' - 10;
else
digit += '0';
*str++ = digit;
} while (n != 0 && ++cnt < places);
}
*str = '\0';
return start;
}
               (
geocities.com/jadoxa)