Pecah pecahan bulat

22

Ketika Anda mengonversi pecahan menjadi angka desimal dan Anda ingin menyimpan angka itu, Anda sering harus membulatkannya, karena Anda hanya ingin menggunakan sejumlah memori. Katakanlah Anda hanya dapat menyimpan 5 angka desimal, kemudian 5/3 menjadi 1.6667. Jika Anda dapat menyimpan hanya 2 digit desimal, itu akan menjadi 1,7 (sekarang dengan asumsi selalu antara 0 dan 9,99 ...).

Jika Anda sekarang mencoba untuk membalik proses itu dengan 1,7 dan Anda ingin mendapatkan pecahan Anda kembali, itu bisa sulit, karena Anda tahu bahwa 1,7 hanya angka bulat. Tentu saja Anda dapat mencoba 17/10 tapi itu fraksi yang agak 'jelek' dibandingkan dengan 5/3 yang 'elegan'.

Jadi tujuannya sekarang menemukan pecahan a / b dengan penyebut terkecil b, yang menghasilkan angka desimal bulat ketika dibulatkan dengan benar.

Detail

Input berisi string dengan angka 1 hingga 5 digit antara 0 (termasuk) dan 10 (tidak termasuk) dengan tanda '.' setelah digit pertama. Katakanlah nmenunjukkan jumlah digit. Output harus berupa daftar / larik dua bilangan bulat [numerator, denominator]atau tipe data rasional (Anda dapat membuat sendiri atau menggunakan built-in) di mana pembilangnya tidak negatif dan penyebutnya positif. Pembilang pecahan / penyebut harus sama dengan input ketika dibulatkan dengan benar ke ndigit (yang berarti n-1digit setelah titik desimal).

Pembatasan: hanya satu pernyataan loop yang dibolehkan. Ini berarti bahwa Anda hanya dapat menggunakan satu pernyataan perulangan tunggal (seperti foratau whileatau gotodll. Serta loop fungsional seperti mapatau foldyang menerapkan kode ke setiap elemen daftar / larik) di seluruh kode Anda, tetapi Anda bebas untuk 'menyalahgunakannya' atau gunakan rekursi dll.

Anda harus menulis fungsi. Jika bahasa Anda tidak memiliki fungsi (atau bahkan jika itu), Anda dapat mengasumsikan bahwa input disimpan dalam variabel (atau input melalui stdin) dan cetak hasilnya atau tulis ke file. Jumlah byte terendah menang.

Pembulatan

Pembulatan harus mengikuti aturan pembulatan 'konvensional', yaitu jika angka terakhir yang akan terpotong adalah 5 atau lebih besar, Anda akan dibulatkan ke atas, dan Anda akan dibulatkan ke bawah untuk kasus lain, jadi misalnya:

4.5494 akan dihasilkan saat pembulatan ke

  • 1 digit: 5
  • 2 digit: 4,5
  • 3 digit: 4,55
  • 4 digit: 4,549

Contohnya

Harap sertakan kasus uji berikut dan yang 'menarik' lainnya:

Input 1.7     Output 5/3
Input 0.      Output 0/1
Input 0.001   Output 1/667
Input 3.1416  Output 355/113
cacat
sumber
1
Namun dalam bahasa fungsional tidak ada yang namanya loop. Contoh musuh di haskell repeatmembuat daftar argumennya yang tak terbatas. Sepertinya tidak, tetapi sebenarnya memiliki kompleksitas waktu O (1). Tapi saya kira menyortir setiap kasus secara individual lebih baik daripada tidak mengizinkan bahasa fungsional.
haskeller bangga
3
Saya tidak suka definisi "loop" saat ini. Dalam Python, misalnya, for n in numbers: f(g(n))setara dengan map(f, map(g, numbers)). Versi fungsional menggunakan mapdua kali, haruskah itu benar-benar dianulir?
flornquake
1
@ MartinBüttner Saya berbicara tentang kasus bahwa bahasa fungsional akan dianulir karena ambiguitas
bangga haskeller
1
Saya menyesal karena saya tidak dapat berkontribusi dalam diskusi tersebut karena pengetahuan saya tentang pemrograman fungsional pada dasarnya nol. Jika Anda memiliki solusi yang Anda tidak yakin apakah itu sesuai dengan 'aturan', silakan kirimkan saja! Pada akhirnya itu seharusnya menjadi tantangan yang menyenangkan dan mendidik!
flawr
2
@ Dennis Tidak itu kata-kata yang tidak menguntungkan, Anda dapat mengirimkannya dalam bentuk apa pun yang Anda suka, ide utama di balik paragraf itu adalah bahwa Anda seharusnya tidak memiliki kerugian jika bahasa Anda membutuhkan lebih banyak byte untuk 'membaca' nomor input.
flawr

Jawaban:

4

CJam, 41 40 36 byte

Q'./1=,:L0\{;)_Qd*mo_d2$/LmOQd-}g'/@

Asuum string input disimpan dalam Q, yang secara eksplisit diizinkan oleh pertanyaan. Cobalah online.

Uji kasus

$ for d in 1.7 0. 0.001 3.1416; do cjam <(echo "\"$d\":Q;
> Q'./1=,:L0\{;)_Qd*mo_d2$/LmOQd-}g'/@
> "); echo; done
5/3
0/1
1/667
355/113

Bagaimana itu bekerja

Q'./1=,:L  " Count the number of characters after the dot and store it in L.     ";
0\         " Push 0 (denominator) and swap it with L (dummy value).              ";
{          "                                                                     ";
  ;        " Discard the topmost item from the stack (numerator or dummy value). ";
  )        " Increment the denominator.                                          ";
  _Qd*mo   " Multiply a copy by Double(Q) and round.                             ";
  _d2$/    " Cast a copy to Double and it divide it by the denominator.          ";
  LmO      " Round to L digits.                                                  ";
  Qd       " If the result is not Double(Q),                                     ";
}g         " repeat the loop.                                                    ";
./@        " Push a slash and rotate the denominator on top of it.               ";
Dennis
sumber
15

T-SQL 254

Sementara T-SQL tidak benar-benar cocok untuk hal semacam ini, itu menyenangkan untuk dicoba. Kinerja semakin buruk semakin tinggi penyebutnya. Ini terbatas pada penyebut 1000.

Input adalah variabel float @

WITH e AS(SELECT *FROM(VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9),(0))n(n)),t AS(SELECT ROW_NUMBER()OVER(ORDER BY(SELECT \))N FROM e a,e b,e c,e d)SELECT TOP 1concat(n.n,'/',d.n)FROM t d,t n WHERE round(n.n/(d.n+.0),len(parsename(@,1)))=@ ORDER BY d.n,n.n

Rincian permintaan

WITH                                      -- Start CTE(Common Table Expression)
 e AS(                                    --Create a set of 10 rows
   SELECT *
   FROM(VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9),(0))n(n)
 ),
 t AS(                                    
   SELECT ROW_NUMBER()OVER(ORDER BY(SELECT \))N 
   FROM e a,e b,e c,e d                   --Cross join e to produce 1000 numbered rows
 )
SELECT 
  TOP 1                                   --Grab first result
  concat(n.n,'/',d.n)                     --Build output
FROM t d,t n                              --Cross join t against itself for denominator and numerator
WHERE round(
  n.n/(d.n+.0),                           --Force float division with +.0
  len(parsename(@,1))                     --Get rounding length
  )=@                                     --Filter where the rounded result = input
ORDER BY d.n,n.n                          --Order by denominator then numerator
MickyT
sumber
+1. Aku menyukainya. Saya memasukkan 3.14159dan memberi saya dengan baik355/113
Tom Chantler
1
+1 Saya tidak pernah berharap melihat bahasa SQL di sini !!!
flawr
@ TomChantler Saya menduga maksud Anda akhirnya :)
MickyT
@ flawr Sejujurnya saya tidak berpikir itu akan berhasil .. metode yang sangat kasar sekalipun.
MickyT
12

Haskell, 62 59

andai saja namanya tidak begitu panjang ...

import Data.Ratio
f s=approxRational(read s)$50/10^length s

ini adalah fungsi yang mengembalikan Rationalnilai.

Penjelasan: fungsiapproxRational adalah fungsi yang mengambil angka float dan flo epsilon dan mengembalikan rasional paling sederhana yang ada di jarak epsilon input. pada dasarnya, mengembalikan perkiraan yang paling sederhana dari float ke rasional dalam jarak "kesalahan yang dapat dimaafkan".

mari manfaatkan fungsi ini untuk kita gunakan. untuk ini kita perlu mencari tahu berapa luas float yang membulatkan ke angka yang diberikan. kemudian memasukkan ini ke dalam approxRationalfungsi akan memberi kita jawaban.

mari kita coba 1.7, misalnya. float terendah yang membulatkan ke 1,7 adalah 1,65. lebih rendah tidak akan membulatkan ke 1,7. sama halnya, batas atas float yang membulatkan ke 1.7 adalah 1.75.
kedua batasnya adalah batasnya adalah angka input +/- 0,05. dapat dengan mudah ditunjukkan bahwa jarak ini selalu 5 * 10 ^ -(the length of the input - 1)(-1 karena selalu ada '.' dalam input). dari sini kodenya cukup sederhana.

kasus uji:

*Main> map f ["1.7", "0.001", "3.1416"]
[5 % 3,1 % 667,355 % 113]

sayangnya itu tidak bekerja pada "0." karena fungsi parser Haskell tidak mengenali a .di akhir float. ini dapat diperbaiki untuk 5 byte dengan mengganti read sdengan read$s++"0".

haskeller bangga
sumber
Ini fungsi yang menarik untuk dimiliki. Biasanya fungsi-fungsi seperti itu ada untuk tujuan menemukan pendekatan rasional terbaik ke angka dalam langkah-langkah paling sedikit, yang dapat dibuktikan dicapai dengan menggunakan representasi fraksi lanjutan yang terpotong. Atau, menemukan pecahan dengan penyebut terendah lebih merupakan keingintahuan akademik. Seseorang biasanya tidak berharap untuk menemukannya sebagai fungsi perpustakaan standar.
COTO
4
@COTO Ya, ini Haskell, penuh dengan penelitian akademis.
haskeller bangga
7

Ruby, 127 125 byte

f=->n{b=q=r=(m=n.sub(?.,'').to_r)/d=10**p=n.count('0-9')-1
b=r if(r=(q*d-=1).round.to_r/d).round(p).to_f.to_s==n while d>1
b}

Menentukan fungsi fyang mengembalikan hasilnya sebagai a Rational. Misalnya jika Anda menambahkan kode ini

p f["1.7"]
p f["0."]
p f["0.001"]
p f["3.1416"]

Anda mendapatkan

(5/3)
(0/1)
(1/667)
(355/113)

Loopnya melewati penyebut. Saya mulai dengan fraksi penuh, misalnya 31416/10000untuk contoh terakhir. Kemudian saya mengurangi penyebutnya, menurunkan pembilangnya secara proporsional (dan memutarnya). Jika putaran rasional yang dihasilkan sama dengan nomor input, saya ingat bagian terbaik baru.

Martin Ender
sumber
4

Mathematica, 49 53 karakter

Rationalize[ToExpression@#,5 10^(1-StringLength@#)]&@

Pemakaian:

Rationalize[ToExpression@#,5 10^(1-StringLength@#)]&@"1.7"

Keluaran:

5/3

Kasus uji:

input: 1.7     output: 5/3
input: 0.      output: 0
input: 0.001   output: 1/999
input: 3.1416  output: 355/113

Kasus 0,001 menurut saya aneh; karena fungsi rasionalisasi tidak berfungsi sesuai dengan deskripsinya, ketika tidak menemukan kasus 1/667. Ini harus menampilkan nomor dengan penyebut terkecil yang ada dalam batas yang ditentukan.

Menghitung
sumber
2
haha saya menggunakan solusi yang sama persis. Sayang sekali di Haskell lebih lama. btw, sepertinya solusi Anda tidak mengambil string sebagai input seperti yang dipersyaratkan oleh spec.
haskeller bangga
Tunggu, inputnya berupa string? Sial, itu artinya saya bisa mengeluarkan beberapa hal dari kode.
Tally
Output Anda untuk 0.001tidak cocok dengan OP karena Rationalizetidak di bawah batasan untuk meminimalkan penyebut. Seperti yang saya sebutkan pada haskeller yang bangga, fungsi perkiraan rasional yang dapat meminimalkan penyebut adalah sangat esoterik (singkatnya karena ini adalah cara yang buruk dan tidak efisien untuk memperkirakan angka). Saya biasanya tidak berharap itu menjadi fungsi perpustakaan standar.
COTO
@COTO Menurut dokumen itu tidak meminimalkan denominator sekalipun.
Martin Ender
@ MartinBüttner: Agak menarik yang dihasilkannya 1/999. 999 menjadi penyebut terendah (dapat diterima) hanya untuk kesalahan antara sekitar 1e-6 dan 2e-6. Batas kesalahan jelas 5e-4. Jadi, apa pun yang dilakukan Mathematica dalam kasus itu, itu pasti tidak berfungsi secara spesifik. : P
COTO
4

Python 2.7+, 111 karakter

Bukti bahwa Anda dapat menulis kode mengerikan dalam bahasa apa pun:

def f(s):
 t,e,y=float(s),50*10**-len(s),1;n=d=x=0
 while x|y:n,d=n+x,d+y;a=1.*n/d;x,y=a<t-e,a>t+e
 return n,d

Keluaran

>>> [f(s) for s in ("1.7", "0.", "0.001", "3.1416")]
[(5, 3), (0, 1), (1, 667), (355, 113)]
Steve
sumber
3

APL, 50

2↑⍎⍕(⍎x←⍞){50>|(10*⍴x)×⍺-⍵÷⍨n←⌊.5+⍺×⍵:n ⍵⋄''}¨⍳1e5

Selama Anda tidak menghitung evaldan toStringsebagai loop

Penjelasan

Pendekatannya adalah untuk beralih lebih dari 1 hingga 10.000 sebagai penyebut dan menghitung pembilang yang paling cocok dengan float, kemudian periksa apakah kesalahannya masih dalam batas. Terakhir, pilih pasangan terkecil dari semua fraksi yang ditemukan.

(⍎x←⍞)Ambil input string dari layar, tetapkan ke x, dan evaluasi
⍳1e5Hasilkan array dari 1 hingga 10.000
{...}¨Untuk setiap elemen array, panggil fungsi dengannya dan (⍎x←⍞)dan argumen (loop)

⍺×⍵Mengalikan argumen
⌊.5+Membulatkan (dengan menambahkan 0,5 kemudian membulatkan)
n←Tetapkan untuk n
⍺-⍵÷⍨Membagi dengan argumen kanan, lalu kurangi dari argumen kiri
(10*⍴x)×Lipat ke 10 dengan kekuatan "panjang x"
|Ambil nilai absolut
50>Periksa jika kurang dari 50 (panjangnya x2 lebih daripada jumlah dp, jadi gunakan 50 di sini alih-alih 0,5)
:n ⍵⋄''Jika cek sebelumnya mengembalikan true, maka kembalikan array ndan argumen yang benar, kalau tidak kembalikan string kosong.

⍎⍕ toStringdan kemudian evaluntuk mendapatkan array dari semua angka dalam array
2↑Pilih hanya 2 elemen pertama, yang merupakan pasangan pembilang-penyebut pertama yang ditemukan

TwiNight
sumber
2

GNU dc, 72 byte

Tidak ada loop - dc bahkan tidak memilikinya. Sebaliknya kontrol berasal dari makro ekor tunggal rekursif - idiomatik untuk dc.

?dXAr^d2*sf*sq1sd0[ld1+sd]sD[r1+r]sN[dlf*ld/1+2/dlq>Ndlq<Dlq!=m]dsmxpldp

Keluaran:

$ for n in 1.7 0. 0.001 3.1416; do echo "    n = $n:"; dc unround.dc <<< $n; done
    n = 1.7:
5
3
    n = 0.:
0
1
    n = 0.001:
1
667
    n = 3.1416:
355
113
$ 

Fiuh. Penjelasan sebagian dalam jawaban ini .

Trauma Digital
sumber
2

Mathematica, 111 karakter

f=Module[{a=0,b=1,k},While[Round[a/b,10^-(StringLength[#]-2)]!=(k=ToExpression)@#,If[N[a/b]>k@#,b++,a++]];a/b]&

Cukup sederhana sebenarnya, dan saya tidak berpikir itu konvergen di mana saja secepat solusi lain, karena pembilang dan penyebut hanya bertambah satu per satu. Saya kebanyakan ingin menemukan solusi sederhana untuk ini. Saya harus melihat jawaban lain dan melihat apa yang terjadi di sana.

Keluaran

f/@{"1.7","0.0","0.001","3.1416","3.14"}
{5/3, 0, 1/667, 355/113, 22/7}

Apakah ada orang di sini yang merayakan Hari Perkiraan Pi ?

krs013
sumber
Tidak, saya hanya merayakan hari perkiraan tau. = P Tapi saya hanya memperhatikan bahwa | 355/113 - pi | <10 ^ -6 =)
flawr
2

Applescript,> 300 byte

Saya ingin melakukan ini dalam bahasa yang secara native melakukan jenis pembulatan yang diperlukan. Ternyata Applescript sesuai dengan tagihan. Kemudian saya melihat enum rounding as taught in school, dan tidak bisa menolak menggunakannya, meskipun tidak ada daya saing Applescript untuk tujuan bermain golf:

on u(q)
    set n to 0
    set d to 1
    set x to 0
    set AppleScript's text item delimiters to "."
    set f to 10 ^ (q's text item 2's length)
    repeat until x = q as real
        set x to (round n * f / d rounding as taught in school) / f
        if x < q then set n to n + 1
        if x > q then set d to d + 1
    end repeat
    return {n, d}
end u

log my u("1.7")
log my u("0.")
log my u("0.001")
log my u("3.1416")

Ini bisa bermain golf sedikit lebih, tapi mungkin tidak sepadan.

Keluaran:

(*5, 3*)
(*0, 1*)
(*1, 667*)
(*355, 113*)
Trauma Digital
sumber
2

SM, 151 148 byte

Edit - versi lebih cepat dan lebih pendek

define f(v){s=scale(x=v);for(i=r=1;i<=10^s;i+=1){t=v*i+1/2;scale=0;p=t/=1;scale=s+1;t=t/i+10^-s/2;scale=s;t=t/1-v;if((t*=-1^(t<0))<r){r=t;n=p;d=i}}}

test case yang sama.

Banyak yang mirip dengan versi sebelumnya, tetapi alih-alih mencoba semua kombinasi n / d yang mungkin, kami mendaki bukit di atas sisa v dan quotients mundur dari kelipatan m == v * d dan penyebut d. Sekali lagi ketepatan perhitungannya sama.

Ini tidak terurai:

define f(v)
{
    s= scale(x=v)
    for( i=r=1; i <= 10^s; i+=1 ){
        t= v * i +1/2
        scale=0
        m=t/=1 # this rounded multiple becomes nominator if
               # backward quotient is first closest to an integer
        scale=s+1
        t= t / i +10^-s/2 # divide multiple back by denominator, start rounding again...
        scale=s
        t= t/1 - v # ...rounding done. Signed residue of backward quotient
        if( (t*= -1^(t < 0)) < r ){
            r=t
            n=m
            d=i
        }
    }
}

Versi ini benar-benar hanya memiliki satu loop dan hanya melakukan $ \ Theta \ left (\ operatorname {fractional_decimals} (v) \ right) $ operasi aritmatika.

Asli - versi lambat

Fungsi ini menghitung nominator n terkecil dan penyebut d sedemikian sehingga fraksi n / d dibulatkan ke fractional_decimals (v) digit sama dengan nilai desimal yang diberikan v.

define f(v){s=scale(v);j=0;for(i=r=1;j<=v*10^s;){scale=s+1;t=j/i+10^-s/2;scale=s;t=t/1-v;if((t*=-1^(t<0))<r){r=t;n=j;d=i};if((i+=1)>10^s){i=1;j+=1}};v}

Kasus cobaan:

define o(){ print "Input ",x,"\tOutput ",n,"/",d,"\n" }
f(1.7); o()
> 0
> Input 1.7       Output 5/3
> 0
f(0.); o()
> 0
> Input 0 Output 0/1
> 0
f(0.001); o()
> 0
> Input .001      Output 1/667
> 0
f(3.1416); o()
> 0
> Input 3.1416    Output 355/113
> 0

Dan di sini tidak terurai:

define f(v)
{
    s=scale(x=v) # save in global for later print
    j=0
    # do a full sequential hill-climb over the residues r of v and all possible
    # fractions n / d with fractional_decimals(v) == s precision.
    for( i=r=1; j <= v * 10^s; ){
        scale=s+1
        t= j / i +10^-s/2 # start rounding...
        scale=s
        t= t/1 - v # ...rounding done. New residue, but still signed
        if( (t*= -1^(t < 0)) < r ){ # absolute residue better?
            # climb hill
            r=t
            n=j
            d=i
        }
        if( (i+=1) > 10^s ){ # next inner step. End?
            # next outer step
            i=1
            j+=1
        }
    }
    v
}

Saya akui, saya sedikit curang dengan meniru loop dalam kedua di dalam loop luar tunggal, tetapi tanpa menggunakan pernyataan loop lebih lanjut. Dan itulah mengapa ia melakukan $ \ Theta \ left (v \ operatorname {fractional_decimals} (v) ^ 2 \ right) $ operasi aritmatika.

Franki
sumber
1
Anda mungkin harus memindahkan versi baru ke depan pos
haskeller bangga
@proudhaskeller selesai
Franki
1

C, 233

Ini bekerja dengan memanggil fungsi rasionalisasi r () dengan penyebut awal 1. Fungsi mulai menambah pembilang, dan memeriksa setiap kenaikan apakah nomor yang dihasilkan, ketika dibulatkan ke jumlah digit yang sama seperti aslinya, memiliki string yang sama representasi seperti aslinya. Begitu pembilangnya bertambah sedemikian rupa sehingga hasilnya lebih besar dari yang asli, fungsinya menambah penyebut dan memanggil dirinya sendiri.

Ini tentu saja menggunakan kode yang jauh lebih banyak, tetapi saya pikir semangat masalah membebaskan pendekatan sederhana ini; yang kita tahu, fungsi rationalize internal () dari bahasa modern memiliki banyak loop internal.

Perhatikan bahwa ini tidak berfungsi untuk input "0." karena itu bukan cara standar untuk menulis float, jadi ketika menulis ulang float ke string, hasilnya tidak akan pernah menjadi "0.".

Spesifikasi menginginkan fungsi yang mengembalikan nilai alih-alih hanya mencetak ke layar, karenanya argumen-passing.

Kode (ungolfed):

void r(char* x, int* a, int* b) {
    int i = -1;
    char z[32];
    double v =atof(x);
    while(1) {
        i++;
        double y = ((double)i)/((double)(*b));
        double w;
        sprintf(z, "%.*f", strlen(strchr(x,'.'))-1, y);
        if(strcmp(x, z)==0) {
            *a = i;
            return;
        }
        w = atof(z);
        if(w > v) {
            (*b)++;
            r(x, a, b);
            return;
        }
    }
}

Pemakaian:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char* argv[]) {
    int num;
    int denom = 1; // start with a denominator of 1
    r(argv[1], &num, &denom);
    printf("%d/%d\n", num, denom);
    return 0;
}

Kode golf:

typedef double D;
void r(char*x,int*a,int*b){int i=-1;char z[32];D v=atof(x);while(1){i++;D y=((D)i)/((D)(*b));D w;sprintf(z,"%.*f",strlen(strchr(x,'.'))-1,y);if(!strcmp(x,z)){*a=i;return;}w=atof(z);if(w>v){(*b)++;r(x,a,b);return;}}}
RT
sumber
sebenarnya, dalam implementasi perpustakaan Haskell ( hackage.haskell.org/package/base-4.7.0.1/docs/src/… ), definisi approxRationalmemiliki hanya satu fungsi pembantu rekursif, dan tidak ada perulangan lebih dari itu.
haskeller bangga
baik, saya salah, sebenarnya memiliki dua fungsi pembantu rekursif, tetapi menurut spec tidak apa
bangga haskeller
Saya tidak mencoba mengatakan bahwa solusi siapa pun tidak valid, hanya ingin memposting satu tanpa rasionalisasi bawaan :)
RT
tentu saja, tetapi fakta bahwa definisi itu sendiri tidak memiliki loop bagus, dan ternyata, Anda menulis di posting Anda "untuk semua yang kita tahu, fungsi rasionalisasi internal () bahasa modern memiliki banyak loop internal." jadi saya memeriksanya.
haskeller bangga
Lagi pula, bagaimana solusinya bekerja?
haskeller bangga
1

Pure Bash, 92 byte

Sebagai penjelasan parsial untuk jawaban ini , ini dia porting ke bash:

f=${1#*.}
q=${1//.}
for((n=0,d=1;x-q;x=2*10**${#f}*n/d+1>>1,n+=x<q,d+=x>q));{ :;}
echo $n/$d

Terutama:

  • bash memiliki hitung bilangan bulat saja. Jadi kita dengan tepat skala semuanya hingga 2 * 10 ^ (jumlah digit fraksional).
  • putaran pesta turun ke bilangan bulat terdekat; 2 pada ekspresi di atas adalah agar kita dapat membulatkan ke bilangan bulat terdekat ( atas atau bawah ).
  • Hanya satu putaran
  • kami memeriksa apakah rasional overshoot atau undershoot desimal dan meningkatkan penyebut atau pembilang sesuai.

Keluaran:

$ for n in 1.7 0. 0.001 3.1416; do echo "    n = $n:"; ./unround.sh $n; done
    n = 1.7:
5/3
    n = 0.:
0/1
    n = 0.001:
1/667
    n = 3.1416:
355/113
$ 
Trauma Digital
sumber
Seharusnya intport yang cukup mudah - hanya untuk c
Digital Trauma
1

JavaScript (E6) 85

F=r=>(l=>{for(n=r,d=1;l&&r!=((n=r*d+1/2|0)/d).toFixed(l);d++);})(r.length-2)||[n|0,d]

Tidak disatukan

F=r=>{
  l = r.length-2; // decimal digits
  if (l==0) return [r|0, 1] // if no decimal return the same (conv to number) with denominator 1

  // loop for increasing denominator 
  for(d = 2; 
      r != ( // loop until find an equal result
      // given R=N/D ==> N=R*D
      (n=r*d+1/2|0) // find possible numerator, rounding (+0.5 and trunc)
      /d).toFixed(l); // calc result to given decimals
      d++);
  return [n,d]
}

Uji di konsol FireFox / FireBug

;["1.7","0.","0.001","3.1416","9.9999"].forEach(v => console.log(v,F(v)))

Keluaran

1.7 [5, 3]
0. [0, 1]
0.001 [1, 667]
3.1416 [355, 113]
9.9999 [66669, 6667]
edc65
sumber