19
\$\begingroup\$

We've had a few challenges for base conversion, but all of them seem to apply to integer values. Let's do it with real numbers!

The challenge

Inputs:

  • A real positive number x, expressed in base 10. This can be taken as a double-precision float or as a string. To avoid precision issues, the number can be assumed to be greater than 10−6 and less than 1015.
  • A target base b. This will be an integer from 2 to 36.
  • A number of fractional digits n. This will be an integer from 1 to 20.

Output: the representation of x in base b with n fractional digits.

When computing the output expression, the digits beyond the n-th should be truncated (not rounded). For example, x = 3.141592653589793 in base b = 3 is 10.0102110122..., so for n = 3 the output would be 10.010 (truncation), not 10.011 (rounding).

For x and b that produce a finite number of digits in the fractional part, the equivalent infinite representation (truncated to n digits) is also allowed. For example, 4.5 in decimal can also be represented as 4.49999....

Don't worry about floating point errors.

Input and output format

x will be given without leading zeros. If x happens to be an integer you can assume that it will be given with a zero decimal part (3.0), or without decimal part (3).

The output is flexible. For example, it can be:

  • A string representing the number with a suitable separator (decimal point) between integer and fractional parts. Digits 11, 12 etc (for b beyond 10) can be represented as letters A, B as usual, or as any other distinct characters (please specify).
  • A string for the integer part and another string for the fractional part.
  • Two arrays/lists, one for each part, containing numbers from 0 to 35 as digits.

The only restrictions are that the integer and fractional parts can be told apart (suitable separator) and use the same format (for example, no [5, 11] for the list representing the integer part and ['5', 'B'] for the list representing the fractional part).

Additional rules

Test cases

Output is shown as a string with digits 0, ..., 9, A, ... , Z, using . as decimal separator.

x, b, n                    ->  output(s)

4.5, 10, 5                 ->  4.50000 or 4.49999
42, 13, 1                  ->  33.0 or 32.C
3.141592653589793, 3, 8    ->  10.01021101
3.141592653589793, 5, 10   ->  3.0323221430
1.234, 16, 12              ->  1.3BE76C8B4395
10.5, 2, 8                 ->  1010.10000000 or 1010.01111111
10.5, 3, 8                 ->  101.11111111
6.5817645, 20, 10          ->  6.BCE2680000 or 6.BCE267JJJJ
0.367879441171442, 25, 10  ->  0.94N2MGH7G8
12944892982609, 29, 9      ->  PPCGROCKS.000000000
\$\endgroup\$
3
  • \$\begingroup\$ Let us continue this discussion in chat. \$\endgroup\$ Aug 17, 2017 at 10:49
  • \$\begingroup\$ for 42, 13, 1 can we have 33 instead of 33.0? \$\endgroup\$
    – LiefdeWen
    Aug 17, 2017 at 10:54
  • \$\begingroup\$ @LiefdeWen No, an essential part of the challenge is that the output must have n decimal digits \$\endgroup\$
    – Luis Mendo
    Aug 17, 2017 at 10:56

12 Answers 12

7
\$\begingroup\$

JavaScript (ES8), 81 74 71 bytes

f=
(x,b,n,g=x=>x.toString(b))=>g(x-x%1)+'.'+g(x%1).substr(2,n).padEnd(n,0)
<div oninput=o.textContent=f(+x.value,b.value,n.value)><input id=x><input type=number min=2 max=36 value=10 id=b><input type=number min=1 max=20 value=10 id=n><pre id=o>

Works for x between 1e-6 and 1e21, b from 2 to 36 (exactly as required) and n from 1 to anything from 10 to 48 depending on the base before floating-point errors creep in. Edit: Saved 7 bytes with help from @Birjolaxew. Saved a further 3 bytes with help from @tsh. Previous 74-byte version also worked with negative numbers:

f=
(x,b,n,[i,d]=`${x.toString(b)}.`.split`.`)=>i+`.`+d.slice(0,n).padEnd(n,0)
<div oninput=o.textContent=f(+x.value,b.value,n.value)><input id=x><input type=number min=2 max=36 value=10 id=b><input type=number min=1 max=20 value=10 id=n><pre id=o>

\$\endgroup\$
11
  • 1
    \$\begingroup\$ How does one do base conversion with regex?!? \$\endgroup\$ Aug 17, 2017 at 12:22
  • 1
    \$\begingroup\$ @EriktheOutgolfer I'm not, it's just a golfier (hopefully) way of extracting up to n "digits" from a string. \$\endgroup\$
    – Neil
    Aug 17, 2017 at 12:41
  • \$\begingroup\$ Then what is the core logic of your function? \$\endgroup\$ Aug 17, 2017 at 12:44
  • \$\begingroup\$ @EriktheOutgolfer Why, JavaScript's built-in base conversion function of course. (Hint: look at where I use the base parameter.) \$\endgroup\$
    – Neil
    Aug 17, 2017 at 12:46
  • \$\begingroup\$ Oh it says .toString(b)...dumb me >_< \$\endgroup\$ Aug 17, 2017 at 12:47
5
\$\begingroup\$

Python 2, 153 149 144 137 135 109 bytes

def f(x,b,m):
 i=int(x);s=[];t=[]
 while i:s=[i%b]+s;i/=b
 while m:m-=1;x=x%1*b;t+=[int(x)]
 return s or[0],t

Hadn't noticed I can just return the digits as numbers, so that makes it a lot simpler. Returns two lists of digits, first for the integer part, second for the fractional.

Try it online!

\$\endgroup\$
1
  • \$\begingroup\$ In case it helps: I've added a note that you only need to support numbers greater than 1e-6 (and less than 1e15, as before) \$\endgroup\$
    – Luis Mendo
    Aug 17, 2017 at 14:00
5
\$\begingroup\$

Perl 6, 25 bytes

->\x,\b,\n{+x .base(b,n)}

Try it

Expanded:

-> \x, \b, \n {
  +x            # make sure it is a Numeric
  .base( b, n ) # do the base conversion
}

Note that the space is so that it is parsed as (+x).base(b,n)
not +( x.base(b,n) ).

\$\endgroup\$
1
  • \$\begingroup\$ In case it helps: I've added a note that you only need to support numbers greater than 1e-6 (and less than 1e15, as before) \$\endgroup\$
    – Luis Mendo
    Aug 17, 2017 at 13:59
3
\$\begingroup\$

Mathematica, 158 bytes

since this challenge allready got a very nice answer in mathematica by @KellyLowder, I tried to produce (with a different approach) the exact results as shown in the test cases

ToUpperCase[""<>Insert[StringReplace[ToString@BaseForm[#,p]&/@PadRight[#&@@(d=RealDigits[#,p=#2]),w=(#3+d[[2]])][[;;w]],"\n "<>ToString@p->""],".",d[[2]]+1]]&


input

[12944892982609, 29, 9]

output

PPCGROCKS.000000000

\$\endgroup\$
3
\$\begingroup\$

Ruby, 45 bytes

->x,b,n{(x*b**n).round.to_s(b).insert(~n,?.)}

Why?

Since b^n in base b is 10^n, we multiply x by that number, and then add the decimal point where it belongs.

Try it online!

\$\endgroup\$
1
  • \$\begingroup\$ -1 byte + bugfix by replacing .round with .to_i; this fixes the last digit of the output for ones where it doesn't match the test outputs. -1 more byte by using .insert ~n,?., without parenthesis. \$\endgroup\$
    – Nnnes
    Aug 18, 2017 at 15:45
3
\$\begingroup\$

C (gcc), 157 152 bytes

Needs 64 bits long int for this to work with larger test cases.

-5 bytes thanks to Peter Cordes

#define P r=99;i=l=x;do{z[--r]=48+7*(l%b>9)+l%b;}while(l/=b);printf(z+r)
long i,r,l;char z[99];f(x,b,n)double x;{P;putchar(46);while(n--){x=(x-i)*b;P;}}

Try it online!

edit: a few bytes can be shaved if it's allowed to output two strings separated by a newline separator:

149 bytes:

#define P r=99;i=l=x;do{z[--r]=48+7*(l%b>9)+l%b;}while(l/=b);printf(z+r)
long i,r,l;char z[99];f(x,b,n)double x;{P;puts("");while(n--){x=(x-i)*b;P;}}

edit: this submission is not the longest one, yay!

\$\endgroup\$
8
  • 2
    \$\begingroup\$ You can use printf(z+r) if it doesn't contain any % characters. (This is code-golf; security and good practices go out the window :P). You could also use puts(z+r) to get a newline for free (saving the puts("") in the second version). \$\endgroup\$ Aug 18, 2017 at 2:37
  • \$\begingroup\$ Thanks! I forgot about providing a char* directly as a pattern, this indeed saves quite a few bytes :-) I cannot use puts(z+r) in the second version since that would mean each decimal will be printed on a newline \$\endgroup\$
    – scottinet
    Aug 18, 2017 at 7:13
  • \$\begingroup\$ Ah, that last part wasn't obvious without an ungolfed version with comments. \$\endgroup\$ Aug 18, 2017 at 13:11
  • \$\begingroup\$ float is shorter than double, but it seems the question does require a double or string input. \$\endgroup\$ Aug 18, 2017 at 13:15
  • 1
    \$\begingroup\$ No need for that. Some common implementations of C do have 64-bit long, and according to code-golf rules that's all you need for your answer to be valid. (Also, it's common for C and C++ code-golf answers to assume 64-bit long, since that's what Try It Online uses.) I'd suggest rolling back your edit, and just adding a note like "long must be 64-bit for this to support the larger test-cases." \$\endgroup\$ Aug 18, 2017 at 14:46
2
\$\begingroup\$

Mathematica 47 Bytes

TakeDrop@@r[#,#2,#3+Last@(r=RealDigits)[#,#2]]&

Calling RealDigits twice to first figure out the number of digits to the left of the decimal.

\$\endgroup\$
2
  • \$\begingroup\$ In case it helps: I've added a note that you only need to support numbers greater than 1e-6 (and less than 1e15, as before) \$\endgroup\$
    – Luis Mendo
    Aug 17, 2017 at 13:57
  • 1
    \$\begingroup\$ I thought the question was just asking for TakeDrop@@RealDigits[##]& but then I realized I had misread things - your solution seems optimal. \$\endgroup\$
    – Mark S.
    Aug 17, 2017 at 23:53
2
\$\begingroup\$

SageMath, 68 bytes

def f(n,b,k):y=n.str(b).split('.')+[''];return y[0],(y[1]+'0'*k)[:k]

Try it online!

\$\endgroup\$
1
  • \$\begingroup\$ In case it helps: I've added a note that you only need to support numbers greater than 1e-6 (and less than 1e15, as before) \$\endgroup\$
    – Luis Mendo
    Aug 17, 2017 at 13:59
1
\$\begingroup\$

Jelly, 16 bytes

*×⁵b⁸ḞðṖḣ⁹,ṫø⁹N‘

Try it online!

Note that singletons are printed as the element in the output.

\$\endgroup\$
3
  • \$\begingroup\$ Hey, what happened to your picture? \$\endgroup\$
    – Luis Mendo
    Aug 17, 2017 at 14:31
  • \$\begingroup\$ @LuisMendo some people cannot render it, since it was connected to Facebook \$\endgroup\$
    – Leaky Nun
    Aug 17, 2017 at 14:32
  • \$\begingroup\$ You know you can upload a picture here, right? Those default ones are so impersonal \$\endgroup\$
    – Luis Mendo
    Aug 17, 2017 at 14:35
1
\$\begingroup\$

Haskell, 188 bytes

f=fromIntegral
g 0 _=[]
g n p=g(div n p)p++[mod n p]
z=(!!)(['0'..'9']++['A'..'Z']++['.'])
h x p l|(i,d)<-properFraction x=z<$>(g i p++[36]++(last$g(floor$d*(f p**f l))p:[0<$[1..l]|d==0]))

Try it online!

g converts a number to a list representing that number in a given base

z maps integers to letters (36 = .)

h applies the previous functions to the integer and fractional part of a number.

\$\endgroup\$
1
\$\begingroup\$

Axiom, 566 bytes

c:=alphanumeric()::List Character
f(a:INT,b:PI):List Character==(r:=[];repeat(y:=a rem b;r:=cons(c.(y+1),r);a:=a quo b;a=0=>break);r)
g(x)==floor(x)::INT
F(x)==>for i in 1..#x repeat z:=concat(z,x.i)
w(a:Float,b:PI,n:NNI):String==
  z:="";b<2 or b>36 or a<0=>z
  ip:=g(a);    fp:=g((a-ip)*b^n)
  ipb:=f(ip,b);fpb:=f(fp,b);cnt:=n-#fpb
  for i in 1..cnt repeat fpb:=cons(c.1,fpb)
  F(ipb);z:=concat(z,".");F(fpb)
  z

h(a,b,n)==>(n>=0 and b>0=>(nd123:=10+g(n*log_2(b)/log_2(10));mxv123456:=digits(nd123::PI);res78484:=w(a,b,n);digits(mxv123456);res78484);"")

it was particular difficult this question; afther some time in write something, the right results it seems generate using one macro for preserve digits()... it is not golfed too much...results:

(7) -> h(4.5,10,5)
   (7)  "4.50000"
                                                             Type: String
(8) -> h(42,13,1)
   (8)  "33.0"
                                                             Type: String
(9) -> h(%pi,3,8)
   (9)  "10.01021101"
                                                             Type: String
(10) -> h(%pi,5,10)
   (10)  "3.0323221430"
                                                             Type: String
(11) -> h(1.234,16,12)
   (11)  "1.3BE76C8B4395"
                                                             Type: String
(12) -> h(0.367879441171442,25,10)
   (12)  "0.94N2MGH7G8"
                                                             Type: String
(13) -> h(12944892982609,29,9)
   (13)  "PPCGROCKS.000000000"
                                                             Type: String
(14) -> h(6.5817645,20,10)
   (14)  "6.BCE267JJJJ"
                                                             Type: String

the real target is one function that convert to base 2..36 each Float [that has k:=digits()] or each calculated number as %pi or %e or the division of two float/int as in 1./3. ['oo' digits]

(15) -> h(%pi,13,800)
   (15)
  "3.1AC1049052A2C77369C0BB89CC9883278298358B370160306133CA5ACBA57614B65B410020
  C22B4C71457A955A5155B04A6CB6CC2C494843A8BBBBA9A039B77B34CB0C036CAC761129B3168
  B8BAB860134C419787C911812985646C7AAA3025BAA118B3AB8265CB347852065667291482145
  6C533447BC53A5262177C9985455C395626091A2CC3126B395C91B65B654A1804226197528410
  29A8A4A55CC7937B347B77B5A914127B11C6A57A84510775A9A467819A468B6B74339CC1290B2
  24921C6A771BC2AB6AB41735119C2231545A86399483119AAA5AC34B46B7B5C9089946A364860
  9B26CB0BAC0ABCBA182C12881933AA93C3942C71AA664753989A3C82166BA2109796C4A134607
  59725A72C9117AC980556A147557C319438287226C94725B125753B009387A48AA45CB1960A04
  A064052C00A6069371949872B14590895C555CB01A39B7589824B8621618A8B1971841201A2AB
  B04B80C7534CC1CB079581491995B46C679555316288C82665645A1A600C1A669B865651B6B842470C018B03C1115B3C4306C015C0B45C"
                                                             Type: String
\$\endgroup\$
1
\$\begingroup\$

Axiom, 127 bytes

g(a)==floor(a)::INT;f(a:Float,b:PI,n:NNI):Any==(b<2 or n>28=>%i;x:=g(a);radix(x,b)+radix(g((a-x)*b^n),b)::RadixExpansion b/b^n)

results

(4) -> f(%e,2,10)
   (4)  10.1011011111
                                                   Type: RadixExpansion 2
(5) -> f(%e,3,10)
   (5)  2.2011011212
                                                   Type: RadixExpansion 3
(6) -> f(%e,35,10)
   (6)  2.P4VBNEB51S
                                                  Type: RadixExpansion 35
(7) -> f(1.4,35,10)
   (7)  1.DYYYYYYYYY
                                                  Type: RadixExpansion 35
(8) -> f(%pi,3,8)
   (8)  10.01021101
                                                   Type: RadixExpansion 3
(9) -> f(%pi,5,10)
   (9)  3.032322143
                                                   Type: RadixExpansion 5
(10) -> f(1.234,16,12)
   (10)  1.3BE76C8B4395
                                                  Type: RadixExpansion 16

It has a little problem for final zero example

 f(4.5,10,5)

Would return '4.5' and not '4.50000'

\$\endgroup\$

Your Answer

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

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