The International System of Units is a system of measuring all around the world, except for a few countries including the US. The SI system (or metric system) is based on powers of ten, listed below (note that this is an incomplete table):

femto(f) pico(p) nano(n) micro(μ) milli(m) centi(c) (base unit) hecto(h) kilo(k) mega(M) giga(G) tera(T) peta(P)
10^-15   10^-12  10^-9   10^-6    10^-3    10^-2    10^0        10^2     10^3    10^6    10^9    10^12   10^15

Your job will to be to take in one of these measures and convert it into another.


Input will be a decimal number 10^-16 < x < 2^31 - 1, an SI prefix, a letter representing a quantity, another SI prefix to convert to, and the same quantity letter. Input will be in the format 1234.56 mC to TC, and will always match with the regex ^\d+(\.\d+)? [fpnμmchkMGTP](?'letter'[a-zA-Z]) to [fpnμmchkMGTP](?P=letter)$. You will never have to convert to/from the base unit (10^0)


Output will be the same number as the input, just converted to a new SI prefix. Specifically, the program should convert the number from SI prefix 1 to SI prefix 2 in the input. Examples:

Input: 1 nm to μm
Output: 0.001

Input: 82 kC to cC
Output: 8200000

Input: 6.54 MK to hK
Output: 65400

Input: 2000 MB to GB
Output: 2

This is , so shortest code in bytes wins!

  • 1
    \$\begingroup\$ Another thing, when generating a large/small number in this way, what should the output be? e.g. 1 PB to fB \$\endgroup\$ – FryAmTheEggman Dec 3 '15 at 15:31
  • \$\begingroup\$ @FryAmTheEggman you may assume the input will never produce an output that is larger/smaller than the range of digits that your language can natively handle (I think that is what you are asking). \$\endgroup\$ – GamrCorps Dec 3 '15 at 17:06
  • \$\begingroup\$ Should we use U+00B5 MICRO SIGN or U+03BC GREEK SMALL LETTER MU? Can we choose either one or do we have to support one or both? \$\endgroup\$ – PurkkaKoodari Dec 3 '15 at 17:06
  • \$\begingroup\$ @Pietu1998 either one is fine. \$\endgroup\$ – GamrCorps Dec 3 '15 at 17:12
  • \$\begingroup\$ Are we allowed to output a floating point number even if the result is an integer? \$\endgroup\$ – PurkkaKoodari Dec 3 '15 at 17:27

Pyth, 46 44 bytes

The program assumes that the character µ (U+00B5 MICRO SIGN) is used for the micro sign. On Windows, this is what you get by pressing Alt Gr + M.

The source code contains some unprintables, so here is a reversible xxd hex dump.

0000000: 2a76 684a 637a 645e 542d 466d 406a 4322  *vhJczd^T-Fm@jC"
0000010: 5c72 575d d623 8b9e 53bb 4c09 b275 2233  \rW].#..S.L..u"3
0000020: 3125 7443 6864 3137 2532 744a            1%tChd17%2tJ

Here's a copy-friendly version:

*vhJczd^T-Fm@j1057004749883241517573776978549 31%tChd17%2tJ

You can try the demo or use the test suite. If these links fail, use the copy-friendly version (demo, test suite).

| improve this answer | |

JavaScript (ES7), 113 109 bytes

s=>(p=s.split` `)[m={f:-9,p:-6,n:-3,µ:0,m:3,c:4,h:8,k:9,M:12,G:15,T:18,P:21},0]/(10**(m[p[3][0]]-m[p[1][0]]))

Edit: Apparently we can use µ(U+00B5 MICRO SIGN) for the micro sign now, so replacing with that saves a byte.

For ES6 compatibility (10**(m[p[3][0]]-m[p[1][0]])) can be replaced with Math.pow(10,m[p[3][0]]-m[p[1][0]]) for 114 bytes.


s=>                                  // s = input string
  (p=s.split` `)                     // p = input parts

    // m = dictionary of SI prefix digit powers of 10
    //     the exact values don't matter as long as they are relative to each other

    0]                               // get the number
      /(10**(m[p[3][0]]-m[p[1][0]])) // get the factor to divide by from the difference
                                     //     between the SI prefix powers of 10


This test uses Math.pow instead of ** so that common browsers can test it.

<input type="text" id="input" value="2000 MB to GB" /><button onclick="result.innerHTML=(
  s=>(p=s.split` `)[m={f:-9,p:-6,n:-3,µ:0,m:3,c:4,h:8,k:9,M:12,G:15,T:18,P:21},0]/Math.pow(10,m[p[3][0]]-m[p[1][0]])
)(input.value)">Go</button><pre id="result"></pre>

| improve this answer | |

Haskell, 143 bytes

Call s.

import Data.List
i(n:f:_:[t])=read n*10**(x f-x t)
x=j.flip elemIndex"f  p  n  µ  mc   hk  M  G  T  P".head
j(Just n)=fromIntegral n
| improve this answer | |
  • \$\begingroup\$ I like your "f p n µ mc hk M G T P" trick a lot. +1 \$\endgroup\$ – Lynn Dec 3 '15 at 21:43
  • \$\begingroup\$ I like this trick also. A tip: you can combine x and j with a pattern guard to: x y|Just n<-elemIndex(y!!0)"f p n µ mc hk M G T P"=fromIntegral n. \$\endgroup\$ – nimi Dec 3 '15 at 21:48

CJam, 67 bytes

lS%:I0=dA"fpnµmchkMGTP"[-7 -4WY5 6ABEHK23]+:AAI3=c#C+=AAI1=c#C+=-#/

Try it online!

Uses µ (U+00B5) for the micro sign so make sure you use this in your input instead of the μ (U+03BC) in the question.

My first attempt at using CJam. Feel free to suggest improvements because I'm sure there are many!


Same method as my JavaScript answer.

lS%:P                    e# P = input parts separated by space
0=d                      e# get number as double
    [-7 -4WY5 6ABEHK23]+
      :A                 e# A = array of SI prefixes followed by their (relative) values
   AP3=c                 e# desired SI prefix
     #C+=                e# get SI value of prefix
   AAP1=c                e# current SI prefix
     #C+=-               e# subtract value of prefix
   #                     e# get power of SI prefix difference
/                        e# divide value by 10 ^ SI prefix difference
| improve this answer | |

Ruby (2.2.1), 126 123 bytes

r={f:-9,p:-6,n:-3,µ:0,m:3,c:4,h:8,k:9,M:12,G:15,T:18,P:21};p ($*[0].to_r/10**(r[$*[3][0].to_sym]-r[$*[1][0].to_sym])).to_f

Saved 3 bytes thanks to user81655


# Hash of prefixes to relative powers
p (                      # print (inspect)
  $*[0].to_r             # input value
  / 10 ** (              # divided by 10^...
    r[$*[3][0].to_sym]   # first prefix symbol
    r[$*[1][0].to_sym]   # second prefix symbol
).to_f                   # convert to float for proper format on output

Takes input from the command line e.g ruby si.rb 1 nm to μm

This is my first golf!

| improve this answer | |
  • 1
    \$\begingroup\$ You can save 3 bytes if you use the values {f:-9,p:-6,n:-3,µ:0,m:3,c:4,h:8,k:9,M:12,G:15,T:18,P:21} for the hash table. Also replacing μ with µ (U+00B5) is allowed and will save another byte. \$\endgroup\$ – user81655 Dec 3 '15 at 20:11
  • \$\begingroup\$ @user81655 good advice, thanks! The two mu/micro symbols seem to give the same byte count when I wc -c my file, any idea why that is? \$\endgroup\$ – Chris Dec 4 '15 at 8:47
  • \$\begingroup\$ No problem. Your file is probably encoded using something like UTF-8 which splits characters above 127 into two bytes. If you switch to a one-byte encoding like ANSI it should remove the extra byte from the file. \$\endgroup\$ – user81655 Dec 4 '15 at 9:17

Haskell, 121 bytes

(e:l:i)!d|e==d=fromEnum l|0<1=i!d
g[(a,b)]=10**fromIntegral(b#1-b#7)*read a

Defines a function f :: String -> Double. For example:

*Main> f "6.54 MK to hK"
| improve this answer | |

Perl 5, 113 + 8 (-p -Mutf8) = 121 bytes

%c=(f,-15,p,-12,n,-9,µ,-6,'m',-3,c,-2,h,2,k,3,M,6,G,9,T,12,P,15);/([0-9.]+) (.).*(.)./;$_=$1*10**($c{$2}-$c{$3})

Try it online!

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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