Bagaimana saya bisa membulatkan nilai float (seperti 37.777779) ke dua tempat desimal (37,78) di C?
Bagaimana saya bisa membulatkan nilai float (seperti 37.777779) ke dua tempat desimal (37,78) di C?
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:
#include <math.h>
float val = 37.777779;
float rounded_down = floorf(val * 100) / 100; /* Result: 37.77 */
float nearest = roundf(val * 100) / 100; /* Result: 37.78 */
float rounded_up = ceilf(val * 100) / 100; /* Result: 37.78 */
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 .
doubles
entah bagaimana? Sepertinya tidak melakukan pekerjaan yang saya inginkan :( (menggunakan floor
dan ceil
).
Menggunakan % .2f di printf. Hanya mencetak 2 titik desimal.
Contoh:
printf("%.2f", 37.777779);
Keluaran:
37.77
float
jangkauan karena val * 100
bisa meluap.
Dengan asumsi Anda berbicara tentang nilai untuk mencetak, maka jawaban Andrew Coleson dan AraK benar:
printf("%.2f", 37.777779);
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.
printf("%.*f", (int)precision, (double)number);
Bagaimana dengan ini:
float value = 37.777779;
float rounded = ((int)(value * 100 + .5) / 100.0);
printf("%.2f", 37.777779);
Jika Anda ingin menulis ke C-string:
char number[24]; // dummy size, you should take care of the size!
sprintf(number, "%.2f", 37.777779);
Tidak ada cara untuk membulatkan a float
ke yang lain float
karena yang dibulatkan float
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.
float
ke n tempat desimal dan kemudian mengharapkan hasilnya selalu memiliki n tempat desimal. Anda masih akan mendapatkan float
, bukan yang Anda harapkan.
Juga, jika Anda menggunakan C ++, Anda bisa membuat fungsi seperti ini:
string prd(const double x, const int decDigits) {
stringstream ss;
ss << fixed;
ss.precision(decDigits); // set # places after decimal
ss << x;
return ss.str();
}
Anda kemudian dapat mengeluarkan ganda myDouble
dengan n
tempat setelah titik desimal dengan kode seperti ini:
std::cout << prd(myDouble,n);
Anda masih dapat menggunakan:
float ceilf(float x); // don't forget #include <math.h> and link with -lm.
contoh:
float valueToRound = 37.777779;
float roundedValue = ceilf(valueToRound * 100) / 100;
Di C ++ (atau dalam C dengan gips gaya-C), Anda bisa membuat fungsi:
/* Function to control # of decimal places to be output for x */
double showDecimals(const double& x, const int& numDecimals) {
int y=x;
double z=x-y;
double m=pow(10,numDecimals);
double q=z*m;
double r=round(q);
return static_cast<double>(y)+(1.0/m)*r;
}
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.
Selalu gunakan printf
keluarga fungsi untuk ini. Bahkan jika Anda ingin mendapatkan nilai sebagai float, Anda sebaiknya menggunakan snprintf
untuk mendapatkan nilai bulat sebagai string dan kemudian menguraikannya kembali dengan atof
:
#include <math.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
double dround(double val, int dp) {
int charsNeeded = 1 + snprintf(NULL, 0, "%.*f", dp, val);
char *buffer = malloc(charsNeeded);
snprintf(buffer, charsNeeded, "%.*f", dp, val);
double result = atof(buffer);
free(buffer);
return result;
}
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:
int main(void) {
// This number is EXACTLY representable as a double
double x = 0.01499999999999999944488848768742172978818416595458984375;
printf("x: %.50f\n", x);
double res1 = dround(x, 2);
double res2 = round(100 * x) / 100;
printf("Rounded with snprintf: %.50f\n", res1);
printf("Rounded with round, then divided: %.50f\n", res2);
}
Anda akan melihat output ini:
x: 0.01499999999999999944488848768742172978818416595459
Rounded with snprintf: 0.01000000000000000020816681711721685132943093776703
Rounded with round, then divided: 0.02000000000000000041633363423443370265886187553406
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 menggunakan round(100 * x) / 100
memberi kita 0,02, yang salah. Mengapa? Karena 100 * 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:
int main(void) {
double x = 8631192423766613.0;
printf("x: %.1f\n", x);
double res1 = dround(x, 2);
double res2 = round(100 * x) / 100;
printf("Rounded with snprintf: %.1f\n", res1);
printf("Rounded with round, then divided: %.1f\n", res2);
}
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:
x: 8631192423766613.0
Rounded with snprintf: 8631192423766613.0
Rounded with round, then divided: 8631192423766612.0
Ups. snprintf
Metode kami mengembalikan hasil yang benar lagi, tetapi pendekatan multiply-kemudian-round-kemudian-membagi gagal. Itu karena nilai matematis yang benar dari 8631192423766613.0 * 100
,, 863119242376661300.0
tidak dapat direpresentasikan secara ganda; nilai terdekatnya adalah 863119242376661248.0
. Ketika Anda membagi kembali dengan 100, Anda mendapatkan 8631192423766612.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 gunakan snprintf
sebagai gantinya. Jika itu terasa seperti retasan yang mengerikan bagi Anda, mungkin Anda akan diyakinkan oleh pengetahuan bahwa pada dasarnya itulah yang dilakukan CPython .
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
#include <math.h>
float y = roundf(x * 100.0f) / 100.0f;
Bergantung pada float
implementasi Anda , angka yang tampaknya setengah jalan bukan. sebagai floating-point biasanya berbasis-2. Lebih lanjut, pembulatan yang tepat ke 0.01
kasus-kasus "setengah jalan" terdekat adalah yang paling menantang.
void r100(const char *s) {
float x, y;
sscanf(s, "%f", &x);
y = round(x*100.0)/100.0;
printf("%6s %.12e %.12e\n", s, x, y);
}
int main(void) {
r100("1.115");
r100("1.125");
r100("1.135");
return 0;
}
1.115 1.115000009537e+00 1.120000004768e+00
1.125 1.125000000000e+00 1.129999995232e+00
1.135 1.134999990463e+00 1.139999985695e+00
Meskipun "1,115" adalah "setengah jalan" antara 1,11 dan 1,12, ketika dikonversi menjadi float
, nilainya adalah 1.115000009537...
dan tidak lagi "setengah jalan", tetapi lebih dekat ke 1,12 dan berputar ke titik terdekat float
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 terdekat float
dari1.129999995232...
Meskipun "1,135" adalah "setengah jalan" antara 1,13 dan 1,14, ketika dikonversi menjadi float
, nilainya adalah 1.134999990463...
dan tidak lagi "setengah jalan", tetapi lebih dekat ke 1,13 dan berputar ke terdekatfloat
dari1.129999995232...
Jika kode digunakan
y = roundf(x*100.0f)/100.0f;
Meskipun "1,135" adalah "setengah jalan" antara 1,13 dan 1,14, bila dikonversi ke float
, nilai 1.134999990463...
dan tidak lagi "setengah jalan", tetapi lebih dekat dengan 1,13 tetapi tidak benar putaran ke float
dari 1.139999985695...
karena lebih presisi terbatas float
vs double
. Nilai yang salah ini dapat dianggap benar, tergantung pada tujuan pengkodean.
Saya membuat makro ini untuk pembulatan angka float. Tambahkan di header / file Anda
#define ROUNDF(f, c) (((float)((int)((f) * (c))) / (c)))
Berikut ini sebuah contoh:
float x = ROUNDF(3.141592, 100)
x sama dengan 3,14 :)
double f_round(double dval, int n)
{
char l_fmtp[32], l_buf[64];
char *p_str;
sprintf (l_fmtp, "%%.%df", n);
if (dval>=0)
sprintf (l_buf, l_fmtp, dval);
else
sprintf (l_buf, l_fmtp, dval);
return ((double)strtod(l_buf, &p_str));
}
Ini n
adalah angka desimal
contoh:
double d = 100.23456;
printf("%f", f_round(d, 4));// result: 100.2346
printf("%f", f_round(d, 2));// result: 100.23
dval
sangat besar 3) aneh if
/ else
blok tempat Anda melakukan hal yang persis sama di setiap cabang , dan 4) penggunaan terlalu rumit sprintf
untuk membangun penentu format untuk sprintf
panggilan kedua ; lebih mudah untuk hanya menggunakan .*
dan meneruskan nilai ganda dan jumlah tempat desimal sebagai argumen untuk sprintf
panggilan yang sama .
#define roundz(x,d) ((floor(((x)*pow(10,d))+.5))/pow(10,d))
a = 8.000000
sqrt(a) = r = 2.828427
roundz(r,2) = 2.830000
roundz(r,3) = 2.828000
roundz(r,5) = 2.828430
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:
double Round(double x, int p)
{
if (x != 0.0) {
return ((floor((fabs(x)*pow(double(10.0),p))+0.5))/pow(double(10.0),p))*(x/fabs(x));
} else {
return 0.0;
}
}
Pembulatan ke 2 tempat desimal untuk presentasi hasil dapat dilakukan sebagai:
double val;
// ...perform calculations on val
String(Round(Round(Round(val,8),6),2));
Sebab val = 6.825
, hasilnya 6.83
seperti yang diharapkan.
Sebab val = 6.824999
, hasilnya adalah 6.82
. Di sini asumsinya adalah bahwa perhitungan menghasilkan tepat 6.824999
dan tempat desimal ke-7 adalah nol.
Sebab val = 6.8249999
, hasilnya adalah 6.83
. Tempat desimal ke-7 yang berada 9
dalam kasus ini menyebabkan Round(val,6)
fungsi memberikan hasil yang diharapkan. Untuk kasus ini, mungkin ada sejumlah jejak 9
.
Sebab val = 6.824999499999
, hasilnya adalah 6.83
. Membulatkan ke tempat desimal ke-8 sebagai langkah pertama, yaitu Round(val,8)
menangani satu kasus yang tidak menyenangkan di mana hasil floating point dihitung 6.8249995
, tetapi secara internal direpresentasikan sebagai6.824999499999...
.
Akhirnya, contoh dari pertanyaan ... val = 37.777779
menghasilkan37.78
.
Pendekatan ini selanjutnya dapat digeneralisasi sebagai:
double val;
// ...perform calculations on val
String(Round(Round(Round(val,N+2),N),2));
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.
Kode C sederhana untuk membulatkan angka:
float n = 3.56;
printf("%.f", n);
Ini akan Output:
4
... atau Anda dapat melakukannya dengan cara lama tanpa pustaka:
float a = 37.777779;
int b = a; // b = 37
float c = a - b; // c = 0.777779
c *= 100; // c = 77.777863
int d = c; // d = 77;
a = b + d / (float)100; // a = 37.770000;
Itu tentu saja jika Anda ingin menghapus informasi tambahan dari nomor tersebut.
fungsi ini mengambil angka dan presisi dan mengembalikan angka yang dibulatkan
float roundoff(float num,int precision)
{
int temp=(int )(num*pow(10,precision));
int num1=num*pow(10,precision+1);
temp*=10;
temp+=5;
if(num1>=temp)
num1+=10;
num1/=10;
num1*=10;
num=num1/pow(10,precision+1);
return num;
}
itu mengubah angka floating point ke int dengan menggeser titik dan memeriksa untuk lebih dari lima kondisi.
float
(dandouble
) bukan floating-point desimal - mereka adalah floating-point biner - jadi pembulatan ke posisi desimal tidak ada artinya. Anda dapat membulatkan output.