Bagaimana mencegah ostringstream (atau serupa) mengeluarkan notasi ilmiah TANPA mengatur presisi

Saya tidak percaya saya tidak tahu cara melakukan ini, tetapi apa yang bisa saya katakan, saya tidak bisa melakukannya. Saya hanya mencoba menulis angka dalam format standar (berbeda dengan notasi ilmiah).

Saya telah membaca banyak sekali contoh bagaimana mencapai ini menggunakan "setprecision(...)" dan "fixed" dan sebagainya, tetapi masalahnya adalah ketepatan angka-angka tersebut tidak diketahui pada waktu kompilasi dan memasukkan perkiraan konservatif dengan 'setprecision (...)' meninggalkan tumpukan angka nol yang berlebihan di tempat itu.

Berikut ini contoh apa yang saya cari:

let: tau = 6.2831

tau * 0.000001 -> 0.0000062831
tau * 0.001    -> 0.0062831
tau            -> 6.2831
tau * 1000     -> 6283.1
tau * 1000000  -> 6283100

Saat ini saya mendapatkan:

tau * 0.000001 -> 6.2831e-006
tau * 0.001    -> 0.0062831
tau            -> 6.2831
tau * 1000     -> 6283.1
tau * 1000000  -> 6.2831e+006

Satu-satunya hal yang dapat saya lakukan adalah mengekstrak eksponen ganda kemudian jika eksponen positif 'perbaiki' presisi ke nol, jika tidak, atur presisi ke '-1 * exp'; tapi ini sepertinya cara yang sangat berbelit-belit untuk 'mematikan' notasi ilmiah. Adakah yang tahu cara yang lebih baik?


person user1277997    schedule 19.03.2012    source sumber
comment
Anda jelas akan mendapat masalah dengan mis. tau * 1e-80, mungkin itulah alasan tidak adanya solusi sederhana. — BTW, ada apa dengan tau * 1000000 -> 6.2831e-006?   -  person leftaroundabout    schedule 19.03.2012
comment
Masalahnya adalah sebagian besar pecahan desimal tidak dapat direpresentasikan secara tepat dengan bilangan floating-point biner (yang digunakan oleh hampir setiap implementasi C++ untuk tipe floating point bawaannya). Misalnya di komputer saya, jika saya mendefinisikan float tau = 6.2831, maka nilai sebenarnya adalah 6.283100128173828125. Pemformat tidak tahu bahwa 14 digit terakhir disebabkan oleh kesalahan pembulatan.   -  person Mike Seymour    schedule 19.03.2012
comment
Faktanya, meskipun bisa mewakili 6.2831 dengan tepat, tetap akan sama dengan 6.28310. Tidak ada angka nol yang berlebihan dalam representasi ukuran tetap. Jika Anda ingin membedakan antara 6.2831 dan 6.28310, Anda harus menggunakan std::string.   -  person MSalters    schedule 19.03.2012
comment
Ups, saya punya -006 padahal yang saya maksud adalah +006 di sana. Maaf tentang itu.   -  person user1277997    schedule 19.03.2012
comment
Sekarang memperluas tau * 1e-80 mungkin menimbulkan masalah tetapi saya dapat meyakinkan Anda bahwa itu akan menyebabkan lebih sedikit masalah daripada menulis ...e+80 sebagai parser yang mengkonsumsi output chuck dengan goyah besar setiap kali ia menemukan e. Sejauh yang saya tahu itu akan mengurai 0,0000...001 dengan baik.   -  person user1277997    schedule 19.03.2012


Jawaban (1)


Meskipun sebagian besar pecahan desimal tidak dapat direpresentasikan secara tepat dengan bilangan FP biner, dan merepresentasikan bilangan FP biner secara tepat dapat menghasilkan banyak digit, terdapat algoritme untuk memformat FP biner sebagai bilangan desimal paling sederhana yang akan dibaca kembali sebagai bilangan desimal. FP asli dengan asumsi mode perutean tertentu pada input (yaitu 6.283100128173828125 akan diformat sebagai 6.2831, tetapi nomor FP berikutnya yang dapat diwakili akan diformat dengan 6 atau 7 digit).

Sayangnya, IO yang diformat dalam C++ (dan dalam C) tidak memiliki cara untuk meminta algoritma tersebut diterapkan. %g dari printf dan pengaturan default IOStream adalah yang terdekat yang tersedia, tetapi keduanya digabungkan dengan pilihan otomatis antara notasi tetap dan ilmiah, memerlukan presisi maksimal dan penghapusan akhiran 0 sebenarnya tidak sama (mereka bertindak berdasarkan representasi desimal yang tepat, mereka tidak mencoba menemukan yang paling sederhana).

Perhatikan bahwa algoritma tersebut tidak benar-benar sederhana, mereka memerlukan aritmatika multipresisi.

person AProgrammer    schedule 19.03.2012