10
\$\begingroup\$

Preamble

A common pain-point when working with rational numbers and decimals is how infrequently one can represent their rational number as a clean, non-repeating decimal. Let's solve this by writing a program to decimalize (not to be confused with decimate) them for us!

The Challenge

Given a fraction, check if it can be represented perfectly by a finite decimal number. If it can, output the decimal representation of this fraction. Otherwise, output the input fraction.

Specifics

Input will be provided as two integers within the range of [1, 32767] (Positive Signed Shorts), representing both the Numerator and Denominator. Numbers may be taken in any convenient format or order, including built-in Fraction formats, a single pre-divided floating point number (of a precision that can accurately represent all possible fractions), a deliminated string, an imagine number, etc. The Input is not guaranteed to be in simplified form.

A given Input is "Decimalizable" if the Denominator of the Simplified Fraction contains no prime factors other than 2 or 5.

The Output, given a Decimalizable Input, must be a decimal number. This may be as any convenient format, including a string, char array, or float. It may not be a Fraction type. (Floats are allowed as it is generally trivial to stringify.) Trailing Zeroes are not allowed, though leading zeroes are.

Otherwise, The Output must be Two Numbers signifying the Numerator and Denominator, in any convenient format. The output may not be a decimal number, nor a floating point number. Output may optionally be Simplified.

Test Cases

16327 /     4 = 4081.75
  186 /   400 = 0.465
23164 /   100 = 231.64
32604 / 20000 = 1.6302
22764 / 16384 = 1.389404296875
    3 /     6 = 0.5
    1 /     3 = 1/3
 3598 /  2261 = 514/323
 7725 / 18529 = 7725/18529
21329 /  3774 = 21329/3774
12213 /  2113 = 12213/2113

Rules

\$\endgroup\$
9
  • \$\begingroup\$ Is it OK if the float is sometimes given in scientific notation? For example, Python shows 1/16384 as 6.103515625e-05. \$\endgroup\$
    – xnor
    Sep 6 at 6:34
  • \$\begingroup\$ @xnor So long as it’s accurate like above, sure. \$\endgroup\$
    – ATaco
    Sep 6 at 6:38
  • \$\begingroup\$ Why is 7725/18529 not decimalizable? \$\endgroup\$ Sep 6 at 7:21
  • 1
    \$\begingroup\$ Then perhaps you should change "contains no prime numbers other than 2 or 5" to "has no prime factors other than 2 or 5", as I read the former as meaning that none of the digits in the decimal representation were primes other than 2 or 5. \$\endgroup\$ Sep 6 at 8:20
  • 1
    \$\begingroup\$ So, suggest testcase: 3/6 \$\endgroup\$
    – tsh
    Sep 6 at 10:26

13 Answers 13

11
\$\begingroup\$

Python 3, 34 bytes

lambda n,d:[n/d,(n,d)][n*1e16%d>0]

Try it online!

\$\endgroup\$
0
6
\$\begingroup\$

Vyxal, 86 bitsv2, 10.75 bytes

\/€tǏ25F[|øḋ

Try it Online!

And here I thought I was being clever skipping the challenge text. Takes a fraction as input, which is automatically simplified.

Explained

\/€tǏ25F[|øḋ­⁡​‎‎⁡⁠⁡‏⁠‎⁡⁠⁢‏⁠‎⁡⁠⁣‏⁠‏​⁡⁠⁡‌⁢​‎‎⁡⁠⁤‏⁠‎⁡⁠⁢⁡‏⁠‎⁡⁠⁢⁢‏⁠‎⁡⁠⁢⁣‏⁠‎⁡⁠⁢⁤‏⁠‏​⁡⁠⁡‌⁣​‎‎⁡⁠⁣⁡‏⁠‎⁡⁠⁣⁢‏⁠‎⁡⁠⁣⁣‏⁠‎⁡⁠⁣⁤‏⁠‎⁡⁠⁤⁡‏‏​⁡⁠⁡‌­
\/€           # ‎⁡Push a copy of the input splitted on "/"
   tǏ25F      # ‎⁢Push the prime factorisation of the denominator with 2s and 5s removed.
        [|øḋ  # ‎⁣If that is empty, convert to decimal.
💎

Created with the help of Luminespire.

\$\endgroup\$
4
  • 3
    \$\begingroup\$ +1 "For once I did some googling" ... and so saved the need to read as far as the 4th paragraph of the challenge... <tongue-in-cheek> \$\endgroup\$ Sep 6 at 7:11
  • 2
    \$\begingroup\$ @DominicvanEssen you telling me people actually read challenge texts and don't just jump to the test cases? :p \$\endgroup\$
    – lyxal
    Sep 6 at 7:32
  • \$\begingroup\$ Vyxal Q - why does 25F remove the numbers 2 and 5, instead of removing 25? \$\endgroup\$ Sep 6 at 12:24
  • 1
    \$\begingroup\$ @DominicvanEssen because the 25 is converted to a list of digits. If you wanted to remove 25, you would use 25o \$\endgroup\$
    – lyxal
    Sep 6 at 12:59
5
\$\begingroup\$

Husk, 13 bytes

?¤/i/¤-p*10¹³

Try it online!

Input as two integers (den, num) represented in floating-point format: it's much easier in Husk to convert a float to an int than the other way around.

?¤/i/¤-p*10¹³       # full program
?¤/i/¤-p*10⁰²⁰²     # with arg expansion:
     ¤              # combin: ¤fgxy means f(gx)(gy)
      -             #  f = - = list difference
       p            #  g = p = prime factors
        *10⁰        #  x = 10x arg1
            ²       #  y = arg2
                    # (so this simplifies the fraction,
                    # and returns list of any non-2, non-5 factors
                    # - truthy if there are any, falsy if none)
?                   # fif: ternary if for functions
                    # use the above result to choose from:
 ¤/i             ⁰² #  (if truthy) convert both args to integers & divide
                    #  (Husk outputs real fractions by default)
    /            ⁰² #  (if falsy) just divide
                    #  (keeps input floating-point format)
\$\endgroup\$
4
\$\begingroup\$

Retina 0.8.2, 246 bytes

\d+
$*
^(1+)(\1)*/(\1)*$
1$#2$*1/$#3$*
+`/(1+)\1{9}(0*)$
/$1$+0
+`/(1+)\1{4}(0*)$
$`/$1$+0
+`/(1+)\1(0*)$
$`$`$`$`/$1$+0
+`/(11+)0
/$1$1$1$1$1$1$1$1$1$1
^(1+)(\1)*/(\1)*$
1$#2$*1/$#3$*
1+
$.&
r`^(?<-1>.)+/1(0)+$
$#1$*00$&
r`((?<-2>.)+)/1(0)+$
.$1

Try it online! Link includes faster test cases, as manipulating large numbers in unary is horribly slow. Explanation:

\d+
$*

Convert to unary.

^(1+)(\1)*/(\1)*$
1$#2$*1/$#3$*

Reduce to lowest terms.

+`/(1+)\1{9}(0*)$
/$1$+0

Find all factors of 10 in the denominator.

+`/(1+)\1{4}(0*)$
$`/$1$+0
+`/(1+)\1(0*)$
$`$`$`$`/$1$+0

Find all factors of 5 and 2 in the denominator, and multiply both numerator and denominator by 2 or 5.

+`/(11+)0
/$1$1$1$1$1$1$1$1$1$1

If the denominator turned out to have other factors, then convert it back to unary.

^(1+)(\1)*/(\1)*$
1$#2$*1/$#3$*

Reduce the fraction to lowest terms again, but only if it was converted back to unary.

1+
$.&

Convert to decimal.

r`^(?<-1>.)+/1(0)+$
$#1$*00$&

Prepend zeros to the numerator if the denominator is a larger power of 10.

r`((?<-2>.)+)/1(0)+$
.$1

Divide the numerator by the denominator if it is a power of 10.

179 bytes and faster for exact decimals in Retina 1:

+`.+/(.*50*)$
$.(*2*)/$.($1*2*
+`.+/(.*[2468]0*)$
$.(*5*)/$.($1*5*
+`0(/.+)0$
$1
r`^(?<-1>.)+/1(0)+$
0$#1*0$&
r`((?<-2>.)+)/1(0)+$
.$1
/\//&`\d+
*
^(_+)(\1)*/(\1)*$
$.(_$#2*_)/$#3

Try it online! Link includes test cases. Explanation:

+`.+/(.*50*)$
$.(*2*)/$.($1*2*

Double the numerator and denominator while the latter ends in 50*.

+`.+/(.*[2468]0*)$
$.(*5*)/$.($1*5*

Multiply the numerator and denominator while the latter's last nonzero digit is even.

+`0(/.+)0$
$1

Cancel out any power of 10 common to both numerator and denominator.

r`^(?<-1>.)+/1(0)+$
0$#1*0$&

Prepend zeros to the numerator if the denominator is a larger power of 10.

r`((?<-2>.)+)/1(0)+$
.$1

Divide the numerator by the denominator if it is a power of 10.

/\//&`\d+
*
^(_+)(\1)*/(\1)*$
$.(_$#2*_)/$#3

Otherwise, reduce the fraction to its lowest terms.

\$\endgroup\$
4
\$\begingroup\$

Factor, 44 bytes

[| x y | x 1e16 * y mod 0 > x y / x y /f ? ]

Try it online!

Port of xnor's Python answer. The only difference is that it's shorter to return either a ratio or a float instead of bothering with lists.

\$\endgroup\$
4
\$\begingroup\$

05AB1E, 11 bytes

`s16°*sÖi`/

I/O as a pair \$[nominator,denominator]\$. The output is unsimplified.

Port of @xnor's Python 3 answer.

Try it online or verify all test cases.

Explanation:

`           # Pop the (implicit) input-pair, and push both items separately to the stack
 s          # Swap so the nominator is at the top
  16°       # Push 10000000000000000 (10**16)
     *      # Multiply it to the nominator
      s     # Swap so the denominator is at the top again
       Ö    # Check if the nominator with 16 trailing 0s is divisible by the denominator
        i   # Pop, and if this is truthy:
         `  #  Pop and push the input-pair separated to the stack again
          / #  And divide them
            #  (after which this decimal is output implicitly as result)
            # (implicit else)
            #  (output the implicit input-pair instead)
\$\endgroup\$
3
\$\begingroup\$

JavaScript (ES6), 24 bytes

Unsurprisingly, a port of xnor's method probably leads to the shortest code.

p=>q=>p*1e16%q?[p,q]:p/q

Try it online!


Other approach (43 bytes, invalid)

Expects (p)(q). Returns either [p,q] or p/q.

We multiply by \$5\$ the greatest power of \$2\$ that divides \$q\$ until it's greater than or equal to \$q\$.

This works for all test cases (at the time of posting) but would fail on some others because the input is not guaranteed to be in simplified form.

p=>q=>(g=n=>n>q?[p,q]:n<q?g(n*5):p/q)(q&-q)

Try it online!

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

Wolfram Language (Mathematica), 47 bytes

If[Depth[r=RealDigits@#]>3,#,N[#,Tr[1^#&@@r]]]&

Try it online!

-8 bytes thanx to @att

\$\endgroup\$
3
  • \$\begingroup\$ I don't think you need to specify the precision of N \$\endgroup\$
    – att
    Sep 6 at 9:12
  • \$\begingroup\$ Invert the condition ListQ@Last@#->VectorQ@# \$\endgroup\$
    – att
    Sep 6 at 9:17
  • \$\begingroup\$ Or better, check if Depth[r] is 3 or 4 \$\endgroup\$
    – att
    Sep 6 at 9:19
3
\$\begingroup\$

JavaScript (ES6), 125 bytes

f=s=>{[n,m]=s.split('/').map(v=>+v);for(i=n>m?n:m;i>1;i--)if(!(n%i|m%i))[n,m]=[n/i,m/i];return n*1e16%m?`${n}/${m}`:`${n/m}`}

const testCases = [
  { input: '16327/4',     output: '4081.75'        },
  { input: '186/400',     output: '0.465'          },
  { input: '23164/100',   output: '231.64'         },
  { input: '32604/20000', output: '1.6302'         },
  { input: '22764/16384', output: '1.389404296875' },
  { input: '3/6',         output: '0.5'            },
  { input: '1/3',         output: '1/3'            },
  { input: '3598/2261',   output: '514/323'        },
  { input: '7725/18529',  output: '7725/18529'     },
  { input: '21329/3774',  output: '21329/3774'     },
  { input: '12213/2113',  output: '12213/2113'     },
];

for (let { input, output } of testCases) {
  console.log(input, '=>', f(input) === output); // Equals?
}
.as-console-wrapper { top: 0; max-height: 100% !important; }

\$\endgroup\$
4
  • 1
    \$\begingroup\$ Welcome to Code Golf, and nice answer! \$\endgroup\$ Sep 7 at 21:21
  • \$\begingroup\$ What would this look like when golfed? \$\endgroup\$
    – Someone
    Sep 8 at 15:11
  • \$\begingroup\$ @Someone well, the unit tests are excluded. Just declaration of f is what matters. \$\endgroup\$ Sep 8 at 15:15
  • \$\begingroup\$ Ohh, I see—I didn't realize that last part was test cases. Sorry about that! EDIT: -2 bytes: the f= doesn't need to be counted unless it's recursive (which it isn't). \$\endgroup\$
    – Someone
    Sep 8 at 15:16
2
\$\begingroup\$

C (gcc), 137 bytes

#define W(A,B)for(;d%A<1;q*=10,++c)n*=B,d/=A;
f(a,b){long n=a,d=b,q=1,c=0;W(2,5)W(5,2)printf("%ld%c%0*ld",d?a:n/q,47-!d,c*!d,--d?b:n%q);}

Try it online!

\$\endgroup\$
2
\$\begingroup\$

Charcoal, 40 bytes

≔I⪪S/θ≧÷⌈Φ⊕⌊θ∧ι¬⌈﹪θιθ¿﹪Xχφ§θ¹⪫θ/I∕§θ⁰§θ¹

Try it online! Link is to verbose version of code. Explanation:

≔I⪪S/θ

Input the fraction.

≧÷⌈Φ⊕⌊θ∧ι¬⌈﹪θιθ

Reduce it to its lowest terms.

¿﹪Xχφ§θ¹

If the denominator is not a factor of 10¹⁰⁰⁰...

⪫θ/

... output as a fraction, otherwise...

I∕§θ⁰§θ¹

output as a decimal.

81 bytes for the way overengineered version which works with arbitrary precision positive integer inputs:

≔I⪪S/θW﹪§θ±²§θ±¹⊞θι≧÷⌊θθ≔Xχ⌈E25⌕⮌X⁰↨§θ¹Iι⁰η¿﹪η§θ¹⪫…θ²/⁺÷§θ⁰§θ¹⭆I⁺η﹪קθ⁰÷η§θ¹η⎇κι.

Try it online! Link is to verbose version of code. Explanation:

≔I⪪S/θ

Input the fraction.

W﹪§θ±²§θ±¹⊞θι≧÷⌊θθ

Reduce it to its lowest terms.

≔Xχ⌈E25⌕⮌X⁰↨§θ¹Iι⁰η

Find a power of 10 that the denominator might be a factor of.

¿﹪η§θ¹⪫…θ²/

But if it isn't then just output the fraction.

⁺÷§θ⁰§θ¹⭆I⁺η﹪קθ⁰÷η§θ¹η⎇κι.

Convert the fraction to arbitrary precision decimal.

\$\endgroup\$
2
\$\begingroup\$

Ruby, 30 bytes

Yet another xnor port. Like chunes' Factor port, it's shorter to return a Rational instead of an array if it can't be decimalized.

->n,d{(n*1e16%d>0?1r:1.0)*n/d}

Attempt This Online!

\$\endgroup\$
2
\$\begingroup\$

sclin, 29 bytes

16 17,.N>d Q",_ ="Q0.:"pop"?#

Try it on scline!

Explanation

Prettified code:

16 17,.N>d Q ( ,_ = ) Q 0.: \pop ?#

Numbers in sclin are internally represented as rational numbers.

  • 16 17,.N>d Q represent the fraction as a decimal with 16 and 17 places
  • ( ,_ = ) Q ... ?# check if both representations are equal...
    • 0.: if true, then get decimal representation
    • \pop otherwise, get rational representation
\$\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.