Memformat angka floating point persis seperti desimal

9

Setiap titik mengambang biner dapat diformat dengan tepat dalam desimal. String yang dihasilkan mungkin agak panjang, tetapi itu mungkin. Dalam artikel saya tentang floating point, saya membahas pentingnya presisi, dan sekarang saya ingin fungsi ini. Tantangan ini adalah untuk menulis sebuah program, atau fungsi, yang mengambil nilai floating point sebagai input dan memformat string desimal yang tepat sebagai output.

Untuk memastikan kami bekerja dengan angka floating point yang benar, format yang tepat harus diberikan sebagai input ke program. Format ini akan menjadi dua bilangan bulat Significand Exponent, di mana nilai floating point yang sebenarnya adalah Significand * 2 ^ Exponent. Perhatikan bahwa nilai mana pun bisa negatif.

Spesifik:

  • Kisaran dan ketepatan setidaknya float 32-bit harus didukung (tidak ada input yang akan melampaui itu)
  • Nilai yang diformat desimal harus merupakan representasi yang tepat (cukup dekat untuk menjamin round-tip kembali ke float tidak cukup baik)
  • Kami tidak mempercayai fungsi format floating point pustaka standar untuk menjadi cukup benar atau cukup cepat (mis .:) printf, dan karenanya fungsi itu mungkin tidak digunakan. Anda harus melakukan pemformatan. Fungsi pemformatan / konversi integral diizinkan.
  • Mungkin tidak ada nol di depan atau di belakang, kecuali untuk nol di depan yang diperlukan .jika tidak ada komponen bilangan bulat
  • Fungsi, atau seluruh program, diizinkan.

Contoh:

1 -2 => 0.25
17 -3 => 2.125
-123 11 => -251904
17 50 => 19140298416324608
23 -13 => 0.0028076171875
3 120 => 3987683987354747618711421180841033728
3 -50 => 0.00000000000000266453525910037569701671600341796875
-3 -50 => -0.00000000000000266453525910037569701671600341796875
10 -2 => 2.5
-12345 -3 => -1543.125
0 0 => 0
161 -4 => 10.0625
512 -3 => 64

Kode terpendek menang.

edA-qa mort-ora-y
sumber
3
Apakah penggunaan aritmatika floating point presisi tak terbatas diperbolehkan?
Dennis
2
Jika eksponennya tidak negatif, bisakah kita akhiri .0?
Sp3000
@ Dennis: Ya, aritmatika presisi tetap tak terbatas atau tinggi diizinkan.
edA-qa mort-ora-y
1
Saya pikir itu tidak konsisten. Jika 0.abcbukan nol awal, maka abc.0bukan yang tertinggal.
orlp
1
Ini juga konvensi untuk selalu diakhiri dengan .0angka bulat ketika berhadapan dengan angka floating point. Lihat misalnya Python: str(1.0) == '1.0'versus str(1) == '1'. Logika Anda masih tidak konsisten.
orlp

Jawaban:

3

CJam, 43

r_'-&\ize999rim<s1e3'0e[W%999/(i_L?\+'.*sW%

Cobalah online

Penjelasan:

Program ini bekerja dengan eksponen hingga ± 999, mendekati presisi ganda (64 bit). Ini memisahkan tanda minus (jika ada) dari yang signifikan, mengalikannya dengan 10 999 kemudian melakukan sedikit pergeseran dengan eksponen, yang sekarang merupakan perhitungan yang tepat. Kemudian bantalan ke kiri dengan nol jika hasilnya memiliki kurang dari 1000 digit, memisahkan 999 digit terakhir sebagai bagian fraksional, menghilangkan nol tertinggal dengan mengubah kebalikannya menjadi bilangan bulat, menambahkan titik desimal jika diperlukan, dan meletakkan semuanya kembali bersamaan.

r_         read and duplicate the significand in string form
'-&        keep only the minus sign, if present
\          swap with the other copy of the significand
iz         convert to integer and get absolute value
e999       multiply by 10^999
ri         read the exponent and convert to integer
m<         shift left by it; negative values will shift right
            the result is an exact non-negative integer
s          convert to string
1e3'0e[    pad to the left with zero characters up to length 1000
            longer strings will be left intact
            we need 1 more than 999 for the 0.xxx case
W%         reverse the string
999/       split into slices of length 999
(          take out the first slice (reversed fractional part)
i          convert to integer
            this removes the leading zeros (trailing in reverse)
_L?        if it's zero, replace with an empty string
\+         concatenate back (to the left) with the second slice
'.*        join the with the dot character
            if the fractional part was zero, we only have the second slice
            (reversed integer part) and there is nothing to join
s          convert to string; this is the reversed result without the sign
W%         reverse back

Pada akhirnya, tanda minus (jika ada) dan string terakhir secara otomatis dicetak bersama.

aditsu berhenti karena SE adalah JAHAT
sumber
2

CJam, 50 byte

q~A1$z#\_0>K5?\z:E#@_s'-&oz*\md_sE'0e[W%isW%'.\+Q?

Ini adalah program lengkap yang bertuliskan STDIN. Cobalah online di penerjemah CJam .

Verifikasi semua kasus uji sekaligus.

Dennis
sumber
Berdasarkan komentar Anda, saya berasumsi CJam memiliki ketepatan yang tidak terbatas dan Anda telah menggunakannya di sini? Apakah benar bahwa jawaban ini mencakup input apa pun, bukan hanya float 32-bit? Bisakah kita mendapatkan penjelasan tentang cara kerjanya?
edA-qa mort-ora-y
CJam memiliki presisi tak terbatas untuk bilangan bulat, tetapi hanya mengapung presisi ganda. Saya kalikan dengan kekuatan 20 untuk eksponen positif dan kekuatan 5 untuk yang negatif, dilemparkan ke string dan masukkan titik. Saya akan menambahkan penjelasan rinci dalam beberapa jam.
Dennis
Dan ya, mengingat memori yang cukup, ini harus bekerja untuk input apa pun.
Dennis
10 -2 tampaknya memiliki nol tambahan
aditsu berhenti karena SE adalah JAHAT
@aditsu: Ah ya, satu nol untuk setiap kekuatan 2 ...
Dennis
2

GNU sed + dc, 65

Skor termasuk +1 untuk -ropsi sed .

y/-/_/
s/.*/dc -e"C8k& 2r^*p"/e
s/\\\n//
s/0+$//
s/^(-?)\./\10./

Saya tergoda untuk mengklaim ini dc-hanya jawaban C8k& 2r^*puntuk skor 10, tetapi dcmemiliki beberapa kebiasaan format:

  • tanda -ve _bukan-
  • garis panjang terputus dengan garis miring terbalik
  • nol tambahan harus dihilangkan
  • memimpin 0 untuk |n| < 1harus ditambahkan

Jadi ekspresi dc dibungkus dan dialihkan seduntuk menjaga hal di atas.

Hasil tes:

$ echo "1 -2
17 -3
-123 11
17 50
23 -13
3 120
3 -50
-3 -50
8388608 127
1 -127" | sed -rf float.sed
0.25
2.125
-251904
19140298416324608
0.0028076171875
3987683987354747618711421180841033728
0.00000000000000266453525910037569701671600341796875
-0.00000000000000266453525910037569701671600341796875
1427247692705959881058285969449495136382746624
0.0000000000000000000000000000000000000058774717541114375398436826861112283890933277838604376075437585313920862972736358642578125
$ 
Trauma Digital
sumber
Hmm, saya pikir itu dcmelanggar aturan saya tentang menggunakan fungsi format standar.
edA-qa mort-ora-y
1
@ edA-qamort-ora-y Saya pikir penggunaan dcok, mengingat "aritmatika presisi tetap tidak terbatas atau tinggi diperbolehkan" . dc's pperintah bukanlah ' floating point format fungsi' - itu adalah fungsi cetak presisi sewenang-wenang. Saya mengatur presisi ke 128 tempat desimal ( C8k), yang saya pikir lebih dari cukup untuk float 32bit.
Digital Trauma