The task is to convert a string representing a number in decimal (base 10) representation to duodecimal (base 12). The input is thus a string, the output should be printed.

The input number can be positive and negative, can be integer or rational. The decimal and duodecimal representations will have a finite number of digits after the (duo)decimal point.

The digits for duodecimal should be 0-9, a, b.

The output should not contain trailing zeroes after the duodecimal point and no leading zeroes before the duodecimal point. The duodecimal point should only be printed if the number is non-integer.


input 400 -> output 294

input 14 -> output 12

input 1498 -> output a4a

input -11 -> output -b

input 11.875 -> output b.a6

counter examples

not okay are outputs like "-001a", "00b.300", "1.050".

EDIT: additional assumptions

  • the number can be represented exactly as float
  • there are overall less than 7 digits (excluding a minus and a duodecimal point) needed to represent the result.
  • 4
    \$\begingroup\$ Can you specify a finite number of digits after the (duo)decimal point? How many digits? \$\endgroup\$
    – Arnauld
    Oct 28, 2018 at 22:29
  • 1
    \$\begingroup\$ I downvoted the challenge because I think it's too much "does your language have a base conversion built-in that uses this format?" \$\endgroup\$
    – xnor
    Oct 28, 2018 at 23:31
  • 3
    \$\begingroup\$ I thought that code golf wasn't a competition between languages, but rather between programs within languages. From that perspective, I don't think it's that significant whether a language has a built-in for this. It just makes that language unintersting for this challenge. \$\endgroup\$
    – recursive
    Oct 29, 2018 at 22:12
  • 1
    \$\begingroup\$ It would have been slightly more interesting if, instead of a and b, the output had to use the duodecimal digits and . This would have at least helped with the boring built-in answers \$\endgroup\$
    – Jo King
    Oct 30, 2018 at 1:24

7 Answers 7


Japt, 2 bytes

I feel I must be missing something ...


Try it

  • \$\begingroup\$ Doesn’t round for input 11.825(not entirely sure if it should, but based solely on the example it should be trunctuated to 3 decimal places) \$\endgroup\$
    – Quintec
    Oct 28, 2018 at 22:33
  • \$\begingroup\$ @Quintec, the spec doesn't make any mention of rounding. \$\endgroup\$
    – Shaggy
    Oct 28, 2018 at 22:35
  • \$\begingroup\$ sorry, i had mistyped that example. rounding wasn't supposed to be necessary and that after some (at most 5) digits after the duodecimal point you reach the exact result. \$\endgroup\$
    – pseyfert
    Oct 28, 2018 at 22:39

C (137 134 bytes)

(thanks for the help Kevin Cruijssen)

l=144;main(b,w){float v;scanf("%f",&v);w=v*l*12;for(w<0&&printf("-",w=-w);b<w;b*=12);for(;(b/=12)>l|w;w%=b)printf(".%x"+(b!=l),w/b);}


Pretty much just an implementation of the base-conversion. This is the algorithm in a readable form:

int main() {
    float v;
    int b, w;
    w=v*1728; // Multiply with 1728, so we can use int-arithmetic
    // Deal with the sign
    if (w<0) { 
    // Find largest power of 12 we care about
    while (b<w) {
    // At b == 1728 the fractional part starts.
    while(b > 1728 || w != 0) {
        b /= 12;
        if (b == 1728) {
        w = w % b;

This is then golfed down with fairly standard C-tricks:

  • Symbols without a type are assumed int.
  • Use arguments to main to save variable declarations.
  • First argument to main is initialized to 1 (argc).
  • Use binary operators instead of control-flow structures.
  • The branch in the main loop can be removed by instead calculating an index into a single format-string.
  • Shuffle around some statements and side-effects to save a character here-and-there.
  • Store 144 into a variable, to not type out that constant so often (the shuffling in the previous step makes 144 a better candidate than 1728).

My personal favorite is probably the w<0&&printf("-",w=-w) part, even though it only saves a character or two compared to the next obvious way - by putting the w=-w as an (unused) argument to printf, we save some parenthesis that would otherwise be needed to group two side-effects into a single expression.

  • \$\begingroup\$ Not sure why it was downvoted, because it seems fine by me, so I upvoted. Two small things to golf (-3 bytes): The brackets {} can be removed for the last loop, and you can put the w<0&&printf("-",w=-w) inside the for-loop to save a byte on the semi-colon: l=144;main(b,w){float v;scanf("%f",&v);w=v*l*12;for(w<0&&printf("-",w=-w);b<w;b*=12);for(;(b/=12)>l|w;w%=b)printf(".%x"+(b!=l),w/b);} \$\endgroup\$ Oct 29, 2018 at 12:36
  • \$\begingroup\$ Btw, if you haven't seen it yet: Tips for golfing in C and Tips for golfing in <all languages> might both be interesting to read through. Enjoy your stay! :) \$\endgroup\$ Oct 29, 2018 at 12:38
  • \$\begingroup\$ Thanks, yeah, I don't know why I didn't see at least the braces :) \$\endgroup\$ Oct 29, 2018 at 21:52

Perl 6, 20 bytes


Try it online!

The built-in base method on numbers just does the right thing, including for negative numbers and floats. However, it outputs letters as uppercase, so the lc method call fixes that.


PHP, 96 bytes

It´s lengthy with the decimals, but quite simple with the limited number of digits.


Save to file, run as pipe with -F

requires PHP 7.0:
older PHP has no spaceship or power operators → parse error
PHP 7.1 understands negative string indexes → negative output for positive input

To fix:
Replace "0-"[0<=>$argn] with "-"[$argn>=0] and insert ?:0 before ;. (→97 bytes)
For PHP 5.5, additionaly replace 12**6 with 2985984. (→99 bytes)
For PHP 5.3 or 5.4, additionally replace the part before the first comma with $argn<0?"-":"". (→100 bytes)
For older PHP, use <?=($r=trim(...))?$argn>0?$r:"-$r":0; (→103 bytes)


<?=                             # PHP: echo
"0-"[0<=>$argn],                # 1. print minus (dumped by base_convert) or zero (dumped by trim)
            $argn*12**6         # 2. multiply input with 6**12
        ,10,12)                 # 3. convert to duodecimal
    ,0,-6)                      # 4. first digits are integer part
    .".".substr($r,-6)          # 5. last 6 digits are fraction
,".0")                          # 6. trim: crop by zeroes and point

ZSH with extendedglob, 81 bytes

read l;r=$(([##12]l*12**6));s=${(L)r[1,-7]}.${(L)r[-6,-1]%%0##};echo - ${s%.}

save to file and run in pipe echo "400" | zsh convert.sh

read l         # read from stdin

# $(())        arithmetic mode
# l*12**6      multiply the number by 12^6 (base change can't handle stuff after the decimal point)
# [#12]        print it in base 12
# [##12]       same, without the leading base modifier printed

# (L)          convert letters to lower case
# [1,-7]       pick all but the last 6 characters
# :-0          if these are empty, print 0 instead
# [-6,-1]      and the last 6 characters, respectively
# %            remove the largest trailing match ...
# %%           same, but largest match
# 0##          pattern is an arbitrary amount of zeroes
# .            print a . between the two blocks

# %.           remove trailing .
# -            avoid interpreting a possible minus sign as option
echo - ${s%.}

JS, 17 bytes


When run in the console, Javascript implicitly outputs the result.


05AB1E, 29 bytes


Very ugly and long, but it works.. Both the decimals and negative aren't supported during base conversion in 05AB1E, so I have to do things manually..

Try it online or verify all test cases.


Ä                # Take the absolute value of the (implicit) input
                 #  i.e. -11.875 → 11.875
 6x              # Push 6 and 6 doubled (so 12)
   sm            # Swap them, and take the power of each other: 6**12 → 2985984
     *           # Multiply it by the absolute value of the input
                 #  i.e. 2985984 * 11.875 → 35458560.0
      12B        # Convert it to Base-12:
                 #  i.e. 35458560.0 → "BA60000"
R                # Reverse it
                 #  i.e. "BA60000" → "00006AB"
 6ô              # Split it into chunks of size 6
                 #  i.e. "00006AB" → ["00006A","B"]
   '.ý          '# Join the list by "."
                 #  i.e. ["00006A","B"] → "00006A.B"
      R          # Reverse it back
                 #  i.e. "00006A.B" → "B.A60000"
       0Ü        # Remove any trailing zeros
                 #  i.e. "B.A6"
         '.Ü    '# Then remove any trailing dots
                 #  i.e. "B.A6" remains "B.A6"
                 #  i.e. "294." → "294"
            l    # And convert it to lowercase
                 #  i.e. "B.A6" → "b.a6"
I0‹i             # If the input is smaller than 0:
                 #  i.e. -11.875 → 1 (truthy)
    '-ì         '# Prepend a "-"
                 #  i.e. "b.a6" → "-b.a6"
                 # (And output the top of the stack implicitly as result)
  • 1
    \$\begingroup\$ Pretty ironic that you need to do this all manually, considering 05AB1E was originally created for the purpose of base conversion... \$\endgroup\$ Nov 2, 2018 at 12:49
  • 1
    \$\begingroup\$ @Cowabunghole When I created this solution I had the exact same thought as you indeed. :) I'll ask Adnan what he thinks about it. \$\endgroup\$ Nov 2, 2018 at 12:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.