Convert between SI prefixes

8

2

Introduction

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

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

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!

GamrCorps

Posted 2015-12-03T14:18:51.517

Reputation: 7 058

1Another thing, when generating a large/small number in this way, what should the output be? e.g. 1 PB to fB – FryAmTheEggman – 2015-12-03T15:31:19.620

@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). – GamrCorps – 2015-12-03T17:06:07.493

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?

– PurkkaKoodari – 2015-12-03T17:06:51.270

@Pietu1998 either one is fine. – GamrCorps – 2015-12-03T17:12:55.837

Are we allowed to output a floating point number even if the result is an integer? – PurkkaKoodari – 2015-12-03T17:27:08.617

@Pietu1998 yes, I'll add that – GamrCorps – 2015-12-03T17:42:43.593

In Mathematica: ({a,b,c,d}=StringSplit@#; UnitConvert[Quantity[ToExpression@a,b],d])& – DavidC – 2015-12-03T20:30:46.557

Answers

1

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).

PurkkaKoodari

Posted 2015-12-03T14:18:51.517

Reputation: 16 699

4

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.

Explanation

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
    [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]                               // 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

Test

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>

user81655

Posted 2015-12-03T14:18:51.517

Reputation: 10 181

3

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

Explanation

# Hash of prefixes to relative powers
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 (                      # 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!

Chris

Posted 2015-12-03T14:18:51.517

Reputation: 31

1You 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. – user81655 – 2015-12-03T20:11:25.053

@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? – Chris – 2015-12-04T08:47:46.723

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. – user81655 – 2015-12-04T09:17:20.590

2

Haskell, 143 bytes

Call s.

import Data.List
s=i.words
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

Leif Willerts

Posted 2015-12-03T14:18:51.517

Reputation: 1 060

I like your "f p n µ mc hk M G T P" trick a lot. +1 – Lynn – 2015-12-03T21:43:59.747

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. – nimi – 2015-12-03T21:48:45.620

2

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!

Explanation

Same method as my JavaScript answer.

lS%:P                    e# P = input parts separated by space
0=d                      e# get number as double
  A
  "fpnµmchkMGTP"
    [-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

user81655

Posted 2015-12-03T14:18:51.517

Reputation: 10 181

1

Haskell, 121 bytes

(e:l:i)!d|e==d=fromEnum l|0<1=i!d
b#i="f`pcnfµimlcmhqkrMuGxT{P~"!(b!!i)
g[(a,b)]=10**fromIntegral(b#1-b#7)*read a
f=g.lex

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

*Main> f "6.54 MK to hK"
65400.0

Lynn

Posted 2015-12-03T14:18:51.517

Reputation: 55 648

0

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!

Xcali

Posted 2015-12-03T14:18:51.517

Reputation: 7 671