Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I know that I can parse a string representation of a number in any base into a number using POSIX::strtol:
use POSIX; my ($num, $n_unparsed) = POSIX::strtol('42', 13); print "$num\n"; # prints "54"
I was wondering if there is a simple one-liner to go the other way around -- i.e. output a number into a string in any base. Thanks,

Replies are listed 'Best First'.
Re: Opposite of strtol?
by ambrus (Abbot) on Jun 21, 2009 at 18:51 UTC
    sub tobase { my $radix = int($_[0]); (my $num = int($_[1])) =~ y/-/_/; + chomp(my $str = `dc -e${radix}o${num}p`); $str } print tobase(13, 54), "\n"; # outputs 42

    Update: fixed the syntax errors, I've no idea how I got them there, sorry.

      surely there's a way to avoid `dc`

      -Paul

Re: Opposite of strtol?
by ambrus (Abbot) on Jun 21, 2009 at 22:35 UTC

    This one is longer than a one-liner. Let's call the stringification function from the gmp library.

    First, make sure you have the gmp library and the headers for it installed, eg. if you have a debian linux system, install the libgmp3-dev package. Then make a directory called Math-Tobase-1.0 and enter the following code to a file called Tobase.xs in it:

    #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include <gmp.h> MODULE = Math::Tobase PACKAGE = Math::Tobase SV * tobase(radix, num) int radix long num CODE: ST(0) = sv_newmortal(); if (2 <= radix && radix <= 62 || -36 <= radix && radix <= -2) +{ mpz_t big; mpz_init_set_si(big, num); if (mpz_sizeinbase(big, radix) <= 70) { char buf[72]; mpz_get_str(buf, radix, big); sv_setpv(ST(0), buf); } mpz_clear(big); }

    Then enter the following to a file called lib/Math/Tobase.pm under the Math-Tobase-1.0 directory (create the subdirectories):

    package Math::Tobase; require Exporter; require DynaLoader; our $VERSION = "1.00"; our @ISA = (Exporter::, DynaLoader::); our @EXPORT = "tobase"; bootstrap Math::Tobase::; 1; __END__

    Then enter the following to the Makefile.PL file under the Math-Tobase-1.0 directory.

    use ExtUtils::MakeMaker; WriteMakefile( NAME => "Math::Tobase", VERSION_FROM => "lib/Math/Tobase.pm", LIBS => ["-lgmp"], );

    Now compile the code with the command

    perl Makefile.PL && make

    If all is successful, you can install the module now and get a Math::Tobase module with a tobase function that does what you want.

    Try it out before installing using the command

    perl -I./lib -Iblib/arch -we 'use Math::Tobase; print tobase(13, 54), +"\n";'

    The output shall be 42.

    Update 2011-03-18: see also Re: Module for 128-bit integer math? for a list of bigint modules. See also Re: Convert big number from decimal to hexadecimal where I reuse this code.

    Update 2012-10-16: note to self (as I reference this code frequently): you may want to use XSLoader.

    Update 2013-11-19: for more XS examples, see Re: Perl XS.

      That's a nice little demo of how to set up a simple perl extension - though there are already a number of modules in existence that will do the conversion.

      <plug>
      Math::GMPz already uses the very same gmp library function:
      perl -MMath::GMPz=":mpz" -wle "print Rmpz_get_str(Math::GMPz->new(54), + 13)"
      </plug>

      It's also available with Math::GMP, too (though the Math::GMP function we use is undocumented):
      perl -MMath::GMP=":constant" -wle "print Math::GMP::get_str_gmp(54, 13 +)"
      There are other modules, too, that one could use, but these are the only modules I know of that use the gmp library for the task.

      Cheers,
      Rob

        Wow, I didn't know there were interfaces to GMP other than Math::BigInt::GMP, and that one does not have an interface to this.

        There's also Math::BaseCnv to print numbers in an arbitary base though.

Re: Opposite of strtol?
by ysth (Canon) on Jun 21, 2009 at 19:00 UTC
    Not a one-liner, but fairly straightforward (untested):
    sub ltostr { my ($num, $base) = @_; Carp::croak("Invalid base $base") if $base < 2 || $base > 36; my $neg = $num < 0; $num = -$num if $neg; my $str = ''; do { $str = ( '0'..'9', 'a'..'z' )[ $num % $base ] . $str; $num = int( $num / $base ); } while $num; $str = "-$str" if $neg; return $str; }
Re: Opposite of strtol?
by CountZero (Bishop) on Jun 21, 2009 at 18:57 UTC
    A Perl scalar will pretend to be a number or a string as the circumstances require, so you do not need a "string to/from number" function.

    Actually, POSIX::strtol does not "parse a string representation of a number in any base into a number" but does a "String to (long) integer translation", which is quite different, but in Perl --IMO-- seldom useful.

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

      A Perl scalar will pretend to be a number or a string as the circumstances require, so you do not need a "string to/from number" function.
      You do if you want a non-base-2/8/10/16 string representation.
      Actually, <c>POSIX::strtol<c> does not "parse a string representation of a number in any base into a number" but does a "String to (long) integer translation", which is quite different, but in Perl --IMO-- seldom useful.
      In principle, yes, but if you know it will work with the range of numbers you have, strtol is possibly fine.