Bagaimana saya bisa membulatkan nilai float (seperti 37.777779) ke dua tempat desimal (37,78) di C?
c
floating-point
decimal
Sakib Arifin
sumber
sumber
float
(dandouble
) bukan floating-point desimal - mereka adalah floating-point biner - jadi pembulatan ke posisi desimal tidak ada artinya. Anda dapat membulatkan output.Jawaban:
Jika Anda hanya ingin membulatkan angka untuk tujuan keluaran, maka
"%.2f"
string format memang jawaban yang benar. Namun, jika Anda benar-benar ingin membulatkan nilai floating point untuk perhitungan lebih lanjut, sesuatu seperti yang berikut berfungsi:Perhatikan bahwa ada tiga aturan pembulatan berbeda yang mungkin ingin Anda pilih: pembulatan ke bawah (yaitu, potong setelah dua tempat desimal), dibulatkan ke terdekat, dan pembulatan ke atas. Biasanya, Anda ingin berkeliling ke terdekat.
Seperti yang beberapa orang lain tunjukkan, karena keanehan representasi floating point, nilai-nilai bulat ini mungkin bukan nilai desimal "jelas", tetapi mereka akan sangat dekat.
Untuk lebih banyak (banyak!) Informasi lebih lanjut tentang pembulatan, dan terutama tentang aturan untuk memutuskan untuk pembulatan ke terdekat, lihat artikel Wikipedia tentang Pembulatan .
sumber
doubles
entah bagaimana? Sepertinya tidak melakukan pekerjaan yang saya inginkan :( (menggunakanfloor
danceil
).Menggunakan % .2f di printf. Hanya mencetak 2 titik desimal.
Contoh:
Keluaran:
sumber
float
jangkauan karenaval * 100
bisa meluap.Dengan asumsi Anda berbicara tentang nilai untuk mencetak, maka jawaban Andrew Coleson dan AraK benar:
Tetapi perhatikan bahwa jika Anda bermaksud membulatkan angka menjadi tepat 37,78 untuk penggunaan internal (misalnya untuk membandingkan dengan nilai lain), maka ini bukan ide yang baik, karena cara angka floating point bekerja: Anda biasanya tidak ingin melakukan perbandingan kesetaraan untuk floating point, alih-alih gunakan nilai target +/- nilai sigma. Atau menyandikan angka sebagai string dengan ketepatan yang diketahui, dan bandingkan itu.
Lihat tautan dalam jawaban Greg Hewgill untuk pertanyaan terkait , yang juga mencakup mengapa Anda tidak boleh menggunakan floating point untuk perhitungan keuangan.
sumber
printf("%.*f", (int)precision, (double)number);
Bagaimana dengan ini:
sumber
Jika Anda ingin menulis ke C-string:
sumber
Tidak ada cara untuk membulatkan a
float
ke yang lainfloat
karena yang dibulatkanfloat
mungkin tidak dapat diwakili (batasan angka floating-point). Sebagai contoh, katakanlah Anda membulatkan 37.777779 ke 37.78, tetapi angka representatif terdekat adalah 37.781.Namun, Anda dapat "membulatkan" a
float
dengan menggunakan fungsi format string.sumber
float
ke n tempat desimal dan kemudian mengharapkan hasilnya selalu memiliki n tempat desimal. Anda masih akan mendapatkanfloat
, bukan yang Anda harapkan.Juga, jika Anda menggunakan C ++, Anda bisa membuat fungsi seperti ini:
Anda kemudian dapat mengeluarkan ganda
myDouble
dengann
tempat setelah titik desimal dengan kode seperti ini:sumber
Anda masih dapat menggunakan:
contoh:
sumber
Di C ++ (atau dalam C dengan gips gaya-C), Anda bisa membuat fungsi:
Maka
std::cout << showDecimals(37.777779,2);
akan menghasilkan: 37,78.Jelas Anda tidak benar-benar perlu membuat semua 5 variabel dalam fungsi itu, tetapi saya membiarkannya di sana sehingga Anda dapat melihat logikanya. Mungkin ada solusi yang lebih sederhana, tetapi ini bekerja dengan baik untuk saya - terutama karena ini memungkinkan saya untuk menyesuaikan jumlah digit setelah tempat desimal yang saya butuhkan.
sumber
Selalu gunakan
printf
keluarga fungsi untuk ini. Bahkan jika Anda ingin mendapatkan nilai sebagai float, Anda sebaiknya menggunakansnprintf
untuk mendapatkan nilai bulat sebagai string dan kemudian menguraikannya kembali denganatof
:Saya mengatakan ini karena pendekatan yang ditunjukkan oleh jawaban terpilih saat ini dan beberapa lainnya di sini - mengalikan dengan 100, membulatkan ke bilangan bulat terdekat, dan kemudian membaginya dengan 100 lagi - cacat dalam dua cara:
Untuk menggambarkan jenis kesalahan pertama - arah pembulatan terkadang salah - coba jalankan program ini:
Anda akan melihat output ini:
Perhatikan bahwa nilai yang kami mulai dengan kurang dari 0,015, sehingga jawaban matematis yang benar ketika membulatkannya ke 2 tempat desimal adalah 0,01. Tentu saja, 0,01 tidak persis representable sebagai ganda, tapi kami berharap hasil kami untuk menjadi yang terdekat ganda untuk 0,01. Menggunakan
snprintf
memberi kita hasil itu, tetapi menggunakanround(100 * x) / 100
memberi kita 0,02, yang salah. Mengapa? Karena100 * x
memberi kita persis 1,5 sebagai hasilnya. Mengalikan dengan 100 dengan demikian mengubah arah yang benar untuk dibulatkan.Untuk mengilustrasikan jenis kesalahan kedua - hasilnya kadang-kadang salah karena
* 100
dan/ 100
tidak benar-benar menjadi terbalik satu sama lain - kita dapat melakukan latihan serupa dengan jumlah yang sangat besar:Nomor kami sekarang bahkan tidak memiliki bagian pecahan; ini adalah nilai integer, hanya disimpan dengan tipe
double
. Jadi hasilnya setelah membulatkannya harus angka yang sama dengan yang kita mulai, kan?Jika Anda menjalankan program di atas, Anda akan melihat:
Ups.
snprintf
Metode kami mengembalikan hasil yang benar lagi, tetapi pendekatan multiply-kemudian-round-kemudian-membagi gagal. Itu karena nilai matematis yang benar dari8631192423766613.0 * 100
,,863119242376661300.0
tidak dapat direpresentasikan secara ganda; nilai terdekatnya adalah863119242376661248.0
. Ketika Anda membagi kembali dengan 100, Anda mendapatkan8631192423766612.0
- nomor yang berbeda dengan yang Anda mulai.Mudah-mudahan itu demonstrasi yang cukup bahwa menggunakan
roundf
untuk pembulatan ke sejumlah tempat desimal rusak, dan yang harus Anda gunakansnprintf
sebagai gantinya. Jika itu terasa seperti retasan yang mengerikan bagi Anda, mungkin Anda akan diyakinkan oleh pengetahuan bahwa pada dasarnya itulah yang dilakukan CPython .sumber
Menggunakan
float roundf(float x)
."Putaran fungsi bulat argumen mereka ke nilai integer terdekat dalam format floating-point, pembulatan setengah kasus jauh dari nol, terlepas dari arah pembulatan saat ini." C11dr §7.12.9.5
Bergantung pada
float
implementasi Anda , angka yang tampaknya setengah jalan bukan. sebagai floating-point biasanya berbasis-2. Lebih lanjut, pembulatan yang tepat ke0.01
kasus-kasus "setengah jalan" terdekat adalah yang paling menantang.Meskipun "1,115" adalah "setengah jalan" antara 1,11 dan 1,12, ketika dikonversi menjadi
float
, nilainya adalah1.115000009537...
dan tidak lagi "setengah jalan", tetapi lebih dekat ke 1,12 dan berputar ke titik terdekatfloat
dari1.120000004768...
"1,125" adalah "setengah jalan" antara 1,12 dan 1,13, ketika dikonversi menjadi
float
, nilainya persis1.125
dan "setengah jalan". Hal putaran ke 1,13 karena hubungan dengan aturan bahkan dan putaran ke terdekatfloat
dari1.129999995232...
Meskipun "1,135" adalah "setengah jalan" antara 1,13 dan 1,14, ketika dikonversi menjadi
float
, nilainya adalah1.134999990463...
dan tidak lagi "setengah jalan", tetapi lebih dekat ke 1,13 dan berputar ke terdekatfloat
dari1.129999995232...
Jika kode digunakan
Meskipun "1,135" adalah "setengah jalan" antara 1,13 dan 1,14, bila dikonversi ke
float
, nilai1.134999990463...
dan tidak lagi "setengah jalan", tetapi lebih dekat dengan 1,13 tetapi tidak benar putaran kefloat
dari1.139999985695...
karena lebih presisi terbatasfloat
vsdouble
. Nilai yang salah ini dapat dianggap benar, tergantung pada tujuan pengkodean.sumber
Saya membuat makro ini untuk pembulatan angka float. Tambahkan di header / file Anda
Berikut ini sebuah contoh:
x sama dengan 3,14 :)
sumber
Ini
n
adalah angka desimalcontoh:
sumber
dval
sangat besar 3) anehif
/else
blok tempat Anda melakukan hal yang persis sama di setiap cabang , dan 4) penggunaan terlalu rumitsprintf
untuk membangun penentu format untuksprintf
panggilan kedua ; lebih mudah untuk hanya menggunakan.*
dan meneruskan nilai ganda dan jumlah tempat desimal sebagai argumen untuksprintf
panggilan yang sama .Definisi kode:
Hasil:
sumber
Ijinkan saya mencoba untuk membenarkan alasan saya untuk menambahkan jawaban lain untuk pertanyaan ini. Dalam dunia yang ideal, pembulatan sebenarnya bukan masalah besar. Namun, dalam sistem nyata, Anda mungkin perlu menghadapi beberapa masalah yang dapat mengakibatkan pembulatan yang mungkin tidak seperti yang Anda harapkan. Misalnya, Anda dapat melakukan perhitungan keuangan di mana hasil akhir dibulatkan dan ditampilkan kepada pengguna sebagai 2 tempat desimal; nilai-nilai yang sama disimpan dengan presisi tetap dalam database yang dapat mencakup lebih dari 2 tempat desimal (karena berbagai alasan; tidak ada jumlah tempat yang optimal untuk disimpan ... tergantung pada situasi spesifik yang harus didukung setiap sistem, misalnya barang kecil yang harganya adalah pecahan satu sen per unit); dan, perhitungan floating point dilakukan pada nilai-nilai di mana hasilnya plus / minus epsilon. Saya telah menghadapi masalah ini dan mengembangkan strategi saya sendiri selama bertahun-tahun. Saya tidak akan mengklaim bahwa saya telah menghadapi setiap skenario atau memiliki jawaban terbaik, tetapi di bawah ini adalah contoh pendekatan saya sejauh ini yang mengatasi masalah ini:
Misalkan 6 tempat desimal dianggap sebagai presisi yang cukup untuk perhitungan float / doubles (keputusan arbitrer untuk aplikasi spesifik), menggunakan fungsi / metode pembulatan berikut:
Pembulatan ke 2 tempat desimal untuk presentasi hasil dapat dilakukan sebagai:
Sebab
val = 6.825
, hasilnya6.83
seperti yang diharapkan.Sebab
val = 6.824999
, hasilnya adalah6.82
. Di sini asumsinya adalah bahwa perhitungan menghasilkan tepat6.824999
dan tempat desimal ke-7 adalah nol.Sebab
val = 6.8249999
, hasilnya adalah6.83
. Tempat desimal ke-7 yang berada9
dalam kasus ini menyebabkanRound(val,6)
fungsi memberikan hasil yang diharapkan. Untuk kasus ini, mungkin ada sejumlah jejak9
.Sebab
val = 6.824999499999
, hasilnya adalah6.83
. Membulatkan ke tempat desimal ke-8 sebagai langkah pertama, yaituRound(val,8)
menangani satu kasus yang tidak menyenangkan di mana hasil floating point dihitung6.8249995
, tetapi secara internal direpresentasikan sebagai6.824999499999...
.Akhirnya, contoh dari pertanyaan ...
val = 37.777779
menghasilkan37.78
.Pendekatan ini selanjutnya dapat digeneralisasi sebagai:
di mana N adalah presisi yang harus dipertahankan untuk semua perhitungan menengah pada mengapung / ganda. Ini bekerja pada nilai negatif juga. Saya tidak tahu apakah pendekatan ini benar secara matematis untuk semua kemungkinan.
sumber
Kode C sederhana untuk membulatkan angka:
Ini akan Output:
sumber
... atau Anda dapat melakukannya dengan cara lama tanpa pustaka:
Itu tentu saja jika Anda ingin menghapus informasi tambahan dari nomor tersebut.
sumber
fungsi ini mengambil angka dan presisi dan mengembalikan angka yang dibulatkan
itu mengubah angka floating point ke int dengan menggeser titik dan memeriksa untuk lebih dari lima kondisi.
sumber