/* 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; }