Real base conversion

19

2

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

Luis Mendo

Posted 2017-08-17T09:30:36.037

Reputation: 87 464

Let us continue this discussion in chat.

– Erik the Outgolfer – 2017-08-17T10:49:44.880

for 42, 13, 1 can we have 33 instead of 33.0? – LiefdeWen – 2017-08-17T10:54:55.493

@LiefdeWen No, an essential part of the challenge is that the output must have n decimal digits – Luis Mendo – 2017-08-17T10:56:19.747

Answers

1

Jelly, 16 bytes

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

Try it online!

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

Leaky Nun

Posted 2017-08-17T09:30:36.037

Reputation: 45 011

Hey, what happened to your picture? – Luis Mendo – 2017-08-17T14:31:25.407

@LuisMendo some people cannot render it, since it was connected to Facebook – Leaky Nun – 2017-08-17T14:32:44.097

You know you can upload a picture here, right? Those default ones are so impersonal – Luis Mendo – 2017-08-17T14:35:27.400

7

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>

Neil

Posted 2017-08-17T09:30:36.037

Reputation: 95 035

1How does one do base conversion with regex?!? – Erik the Outgolfer – 2017-08-17T12:22:56.850

@EriktheOutgolfer I'm not, it's just a golfier (hopefully) way of extracting up to n "digits" from a string. – Neil – 2017-08-17T12:41:51.233

Then what is the core logic of your function? – Erik the Outgolfer – 2017-08-17T12:44:37.343

@EriktheOutgolfer Why, JavaScript's built-in base conversion function of course. (Hint: look at where I use the base parameter.) – Neil – 2017-08-17T12:46:20.697

Oh it says .toString(b)...dumb me >_< – Erik the Outgolfer – 2017-08-17T12:47:00.550

-1 bytes by using \. instead of [.]. -4 bytes by using .split instead of .match.

– Birjolaxew – 2017-08-18T07:32:10.423

@Birjolaxew \. doesn't work because it's a string not a regex but I love your idea of appending a .! (You missed some extra savings though.) – Neil – 2017-08-18T07:46:54.057

@Neil Ah right, my mistake. Good catch on those extra savings - I always forget that you can .split\`` (what even is that anyway :P ) – Birjolaxew – 2017-08-18T07:50:06.477

Clever use of $ .split() rather than standard .split(). Otherwise you have to do a short circuit trick and end up gaining one character: f=(r,b,n,[i,d]=r.toString(b).split.)=>i+'.'+(d||'0').slice(0,n).padEnd(n,0) – sintax – 2017-08-18T19:47:04.643

(x,b,n,g=x=>x.toString(b))=>g(x-x%1)+'.'+g(x%1).slice(2,n+2).padEnd(n,0) – tsh – 2017-08-21T04:58:58.727

@tsh Nice but you forgot to use substr. – Neil – 2017-08-21T08:10:30.170

5

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!

Arfie

Posted 2017-08-17T09:30:36.037

Reputation: 1 230

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) – Luis Mendo – 2017-08-17T14:00:00.663

5

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

Brad Gilbert b2gills

Posted 2017-08-17T09:30:36.037

Reputation: 12 713

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) – Luis Mendo – 2017-08-17T13:59:25.147

3

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

J42161217

Posted 2017-08-17T09:30:36.037

Reputation: 15 931

3

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!

scottinet

Posted 2017-08-17T09:30:36.037

Reputation: 981

2You 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). – Peter Cordes – 2017-08-18T02:37:45.383

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 – scottinet – 2017-08-18T07:13:16.180

Ah, that last part wasn't obvious without an ungolfed version with comments. – Peter Cordes – 2017-08-18T13:11:36.627

float is shorter than double, but it seems the question does require a double or string input. – Peter Cordes – 2017-08-18T13:15:57.457

Using floats produce erroneous results, especially for the last test case. Also, to handle 10^15 numbers, if I strictly follow C standards, then I should use long long integers. Here I'm just taking advantage of 64 bits compilers ;-) – scottinet – 2017-08-18T14:34:16.610

I was just thinking of taking full advantage of the "Don't worry about floating point errors." in the question. Of course it reduces precision a lot. :P Anyway, good point that this isn't just C, it's "C with 64-bit long", which is fine as long as you say so in the answer. Note that gcc targeting the 64-bit Windows ABI has 32-bit long, because it's a feature of the ABI, not up to the compiler. (So "gcc" doesn't imply 64-bit long) – Peter Cordes – 2017-08-18T14:37:40.623

Thinking about it, using "64 bits long integers" makes no sense at all and this cannot be justified. I'll edit my answer right now and define proper long long ints. – scottinet – 2017-08-18T14:45:05.267

1No 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." – Peter Cordes – 2017-08-18T14:46:46.913

3

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!

G B

Posted 2017-08-17T09:30:36.037

Reputation: 11 099

-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. – Nnnes – 2017-08-18T15:45:10.720

2

SageMath, 68 bytes

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

Try it online!

Uriel

Posted 2017-08-17T09:30:36.037

Reputation: 11 708

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) – Luis Mendo – 2017-08-17T13:59:33.273

2

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.

Kelly Lowder

Posted 2017-08-17T09:30:36.037

Reputation: 3 225

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) – Luis Mendo – 2017-08-17T13:57:47.210

1I thought the question was just asking for TakeDrop@@RealDigits[##]& but then I realized I had misread things - your solution seems optimal. – Mark S. – 2017-08-17T23:53:48.897

1

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.

jferard

Posted 2017-08-17T09:30:36.037

Reputation: 1 764

1

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'

RosLuP

Posted 2017-08-17T09:30:36.037

Reputation: 3 036

1

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

RosLuP

Posted 2017-08-17T09:30:36.037

Reputation: 3 036