ฉันจะปัดเศษทศนิยม (เช่น 37.777779) เป็นทศนิยมสองตำแหน่ง (37.78) ใน C ได้อย่างไร
ฉันจะปัดเศษทศนิยม (เช่น 37.777779) เป็นทศนิยมสองตำแหน่ง (37.78) ใน C ได้อย่างไร
คำตอบ:
หากคุณต้องการปัดเศษตัวเลขเพื่อเอาท์พุท"%.2f"
สตริงรูปแบบจะเป็นคำตอบที่ถูกต้อง อย่างไรก็ตามหากคุณต้องการปัดเศษค่าทศนิยมสำหรับการคำนวณต่อไปสิ่งที่คล้ายกับงานต่อไปนี้:
#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 */
โปรดสังเกตว่ามีกฎการปัดเศษที่แตกต่างกันสามข้อที่คุณอาจต้องการเลือก: ปัดเศษลง (เช่นตัดหลังทศนิยมสองตำแหน่ง) ปัดเศษให้ใกล้ที่สุดและปัดเศษขึ้น โดยปกติแล้วคุณต้องการรอบที่ใกล้ที่สุด
ในขณะที่คนอื่นหลายคนชี้ให้เห็นเนื่องจากองค์ประกอบของการเป็นตัวแทนจุดลอยตัวค่าที่ปัดเศษเหล่านี้อาจไม่ตรงกับค่าทศนิยม "ชัดเจน" แต่พวกเขาจะอยู่ใกล้มาก
สำหรับมาก (มาก!) ข้อมูลเพิ่มเติมเกี่ยวกับการปัดเศษและโดยเฉพาะอย่างยิ่งในการผูกทำลายกฎระเบียบสำหรับการปัดเศษที่ใกล้ที่สุดให้ดูบทความวิกิพีเดียปัดเศษ
doubles
หรือไม่ ดูเหมือนจะไม่ทำงานที่ฉันต้องการ :( (ใช้floor
และceil
)
การใช้% .2fใน printf มันพิมพ์ทศนิยม 2 ตำแหน่งเท่านั้น
ตัวอย่าง:
printf("%.2f", 37.777779);
เอาท์พุท:
37.77
float
ช่วงที่val * 100
สามารถล้นได้
สมมติว่าคุณกำลังพูดถึงรอบค่าสำหรับการพิมพ์แล้ว คำตอบของAndrew ColesonและAraKนั้นถูกต้อง:
printf("%.2f", 37.777779);
แต่โปรดทราบว่าหากคุณต้องการปัดเศษตัวเลขให้เป็น 37.78 สำหรับการใช้ภายใน (เช่นเพื่อเปรียบเทียบกับค่าอื่น) นี่ไม่ใช่ความคิดที่ดีเนื่องจากวิธีการทำงานของตัวเลขจุดลอยตัว: โดยปกติคุณจะไม่ ต้องการเปรียบเทียบความเท่าเทียมกันสำหรับ floating point แทนใช้ค่าเป้าหมาย +/- a sigma value หรือเข้ารหัสตัวเลขเป็นสตริงด้วยความแม่นยำที่รู้จักและเปรียบเทียบ
ดูลิงก์ในคำตอบของ Greg Hewgill สำหรับคำถามที่เกี่ยวข้องซึ่งครอบคลุมถึงสาเหตุที่คุณไม่ควรใช้ทศนิยมสำหรับการคำนวณทางการเงิน
printf("%.*f", (int)precision, (double)number);
เกี่ยวกับสิ่งนี้:
float value = 37.777779;
float rounded = ((int)(value * 100 + .5) / 100.0);
printf("%.2f", 37.777779);
หากคุณต้องการเขียนถึง C-string:
char number[24]; // dummy size, you should take care of the size!
sprintf(number, "%.2f", 37.777779);
ไม่มีวิธีการปัด a float
ไปยังอีกfloat
เนื่องจากการปัดเศษfloat
อาจไม่สามารถแทนได้ (ข้อ จำกัด ของตัวเลขทศนิยม) ตัวอย่างเช่นสมมติว่าคุณปัด 37.777779 ถึง 37.78 แต่จำนวนที่สามารถแทนได้ที่ใกล้ที่สุดคือ 37.781
อย่างไรก็ตามคุณสามารถ "ปัดเศษ" a float
โดยใช้ฟังก์ชันสตริงรูปแบบ
float
ทศนิยมa ถึง n แล้วคาดว่าผลลัพธ์จะมีทศนิยมสิบตำแหน่งเสมอ คุณจะยังคงได้รับfloat
ไม่ใช่เพียงที่คุณคาดหวัง
นอกจากนี้หากคุณใช้ C ++ คุณสามารถสร้างฟังก์ชันดังนี้:
string prd(const double x, const int decDigits) {
stringstream ss;
ss << fixed;
ss.precision(decDigits); // set # places after decimal
ss << x;
return ss.str();
}
จากนั้นคุณสามารถเอาท์พุทสองครั้งที่myDouble
มีn
สถานที่หลังจุดทศนิยมด้วยรหัสเช่นนี้:
std::cout << prd(myDouble,n);
คุณยังสามารถใช้:
float ceilf(float x); // don't forget #include <math.h> and link with -lm.
ตัวอย่าง:
float valueToRound = 37.777779;
float roundedValue = ceilf(valueToRound * 100) / 100;
ใน C ++ (หรือ C กับ C-style cast) คุณสามารถสร้างฟังก์ชั่น:
/* 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;
}
จากนั้นstd::cout << showDecimals(37.777779,2);
จะสร้าง: 37.78
เห็นได้ชัดว่าคุณไม่จำเป็นต้องสร้างตัวแปรทั้ง 5 ตัวในฟังก์ชั่นนั้น แต่ฉันทิ้งไว้ที่นั่นเพื่อให้คุณเห็นตรรกะ อาจจะมีวิธีแก้ไขที่ง่ายกว่า แต่วิธีนี้ใช้ได้ผลดีสำหรับฉัน - โดยเฉพาะอย่างยิ่งเนื่องจากช่วยให้ฉันสามารถปรับจำนวนตัวเลขหลังตำแหน่งทศนิยมตามที่ฉันต้องการ
ใช้printf
ตระกูลของฟังก์ชันนี้เสมอ แม้ว่าคุณต้องการรับค่าเป็นทุ่นคุณก็ควรปิดใช้งานsnprintf
เพื่อให้ได้ค่าที่ถูกปัดเศษเป็นสตริงแล้วจึงวิเคราะห์ด้วย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;
}
ฉันพูดแบบนี้เพราะวิธีที่แสดงโดยคำตอบที่ได้รับคะแนนสูงสุดในขณะนี้และอื่น ๆ อีกมากมายที่นี่ - คูณด้วย 100 การปัดเศษเป็นจำนวนเต็มที่ใกล้ที่สุดแล้วหารด้วย 100 อีกครั้ง - มีข้อบกพร่องสองวิธี:
เพื่อแสดงข้อผิดพลาดชนิดแรก - ทิศทางการปัดเศษบางครั้งผิด - ลองเรียกใช้โปรแกรมนี้:
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);
}
คุณจะเห็นผลลัพธ์นี้:
x: 0.01499999999999999944488848768742172978818416595459
Rounded with snprintf: 0.01000000000000000020816681711721685132943093776703
Rounded with round, then divided: 0.02000000000000000041633363423443370265886187553406
โปรดทราบว่าค่าที่เราเริ่มด้วยน้อยกว่า 0.015 ดังนั้นคำตอบที่ถูกต้องทางคณิตศาสตร์เมื่อปัดเศษเป็นทศนิยม 2 ตำแหน่งคือ 0.01 แน่นอน 0.01 ไม่สามารถแทนได้อย่างแน่นอนเป็นสองเท่า แต่เราคาดว่าผลลัพธ์ของเราจะเป็นสองเท่าที่ใกล้เคียงที่สุดถึง 0.01 การใช้snprintf
ให้ผลลัพธ์กับเรา แต่การใช้round(100 * x) / 100
ให้เรา 0.02 ซึ่งผิด ทำไม? เพราะ100 * x
ให้ผลที่ตรงกับเรา 1.5 เท่า การคูณด้วย 100 จึงเปลี่ยนทิศทางที่ถูกต้องเป็นปัด
เพื่อแสดงให้เห็นข้อผิดพลาดประเภทที่สอง - บางครั้งผลลัพธ์ที่ผิดเนื่องจาก* 100
และ/ 100
ไม่ได้เป็นผู้รุกรานซึ่งกันและกันอย่างแท้จริง - เราสามารถทำแบบฝึกหัดที่คล้ายกันซึ่งมีจำนวนมาก:
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);
}
หมายเลขของเราตอนนี้ไม่มีแม้แต่เศษส่วน double
มันเป็นค่าจำนวนเต็มเพียงเก็บไว้กับประเภท ผลลัพธ์หลังจากการปัดเศษควรเป็นจำนวนเดียวกับที่เราเริ่มด้วยใช่ไหม
หากคุณเรียกใช้โปรแกรมข้างต้นคุณจะเห็น:
x: 8631192423766613.0
Rounded with snprintf: 8631192423766613.0
Rounded with round, then divided: 8631192423766612.0
อุ่ย snprintf
วิธีการของเราส่งคืนผลลัพธ์ที่ถูกต้องอีกครั้ง แต่วิธีการคูณหลาย ๆ รอบแล้วหารไม่สำเร็จ นั่นเป็นเพราะค่าที่ถูกต้องทางคณิตศาสตร์ของ8631192423766613.0 * 100
, 863119242376661300.0
ไม่สามารถแทนได้ว่าเป็นสองเท่า 863119242376661248.0
ค่าที่ใกล้ที่สุดคือ เมื่อคุณหารมันกลับด้วย 100 คุณจะได้8631192423766612.0
จำนวนที่แตกต่างจากที่คุณเริ่มต้น
หวังว่านั่นเป็นการสาธิตที่เพียงพอว่าการใช้roundf
การปัดเศษทศนิยมจำนวนหนึ่งจะใช้งานไม่ได้และคุณควรใช้snprintf
แทน ถ้าที่รู้สึกเหมือนสับที่น่ากลัวกับคุณบางทีคุณอาจจะได้รับความมั่นใจจากความรู้ที่ว่ามันเป็นสิ่งที่ไม่ CPython
ใช้ float roundf(float x)
ใช้
"ฟังก์ชั่นการปัดเศษปัดเศษอาร์กิวเมนต์เป็นค่าจำนวนเต็มที่ใกล้ที่สุดในรูปแบบทศนิยมโดยการปัดครึ่งกรณีออกจากศูนย์โดยไม่คำนึงถึงทิศทางการปัดเศษในปัจจุบัน" C11dr §7.12.9.5
#include <math.h>
float y = roundf(x * 100.0f) / 100.0f;
ขึ้นอยู่กับfloat
การใช้งานของคุณตัวเลขที่อาจดูเหมือนว่าครึ่งทางไม่ใช่ จุดลอยตัวโดยทั่วไปจะเป็นเบส -2 ยิ่งไปกว่านั้นการปัดเศษไปยังตำแหน่งที่ใกล้ที่สุด0.01
ในทุกกรณี "ครึ่งทาง" เป็นสิ่งที่ท้าทายที่สุด
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
แม้ว่า "1.115" คือ "half-way" ระหว่าง 1.11 ถึง 1.12 เมื่อแปลงfloat
เป็นค่าจะเป็น1.115000009537...
และไม่เป็น "half-way" อีกต่อไป แต่ใกล้ถึง 1.12 และใกล้เคียงที่สุดfloat
ของ1.120000004768...
"1.125" คือ "half-way" ระหว่าง 1.12 ถึง 1.13 เมื่อถูกแปลงfloat
เป็นค่าจะถูกต้อง1.125
และคือ "half-way" มันปัดเศษไปที่ 1.13 เนื่องจากความสัมพันธ์กับกฏและรอบที่ใกล้เคียงที่สุดfloat
ของ1.129999995232...
แม้ว่า "1.135" จะเป็น "half-way" ระหว่าง 1.13 ถึง 1.14 แต่เมื่อแปลงfloat
เป็นค่าจะเป็น1.134999990463...
และไม่ใช่ "half-way" อีกต่อไป แต่ใกล้ถึง 1.13 และปัดเศษให้ใกล้เคียงที่สุดfloat
ของ1.129999995232...
หากใช้รหัส
y = roundf(x*100.0f)/100.0f;
แม้ว่า "1.135" คือ "ครึ่งทาง" ระหว่าง 1.13 และ 1.14 เมื่อแปลงfloat
ค่าเป็น1.134999990463...
และไม่เป็น "ครึ่งทาง" แต่ใกล้ชิดกับ 1.13 แต่ไม่ถูกต้องรอบfloat
ของ1.139999985695...
เนื่องจากความแม่นยำที่ จำกัด มากขึ้นของfloat
เทียบกับdouble
. ค่าที่ไม่ถูกต้องนี้อาจถูกมองว่าถูกต้องขึ้นอยู่กับเป้าหมายในการเขียนโค้ด
ฉันสร้างมาโครนี้เพื่อปัดเศษจำนวนลอย เพิ่มไว้ในส่วนหัว / ไฟล์ของคุณ
#define ROUNDF(f, c) (((float)((int)((f) * (c))) / (c)))
นี่คือตัวอย่าง:
float x = ROUNDF(3.141592, 100)
x เท่ากับ 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));
}
นี่n
คือจำนวนทศนิยม
ตัวอย่าง:
double d = 100.23456;
printf("%f", f_round(d, 4));// result: 100.2346
printf("%f", f_round(d, 2));// result: 100.23
dval
มีขนาดใหญ่ 3) แปลกif
/ else
บล็อกที่คุณทำสิ่งเดียวกันในแต่ละสาขา และ 4) การใช้ที่ซับซ้อนของsprintf
การสร้างตัวระบุรูปแบบสำหรับการsprintf
โทรครั้งที่สอง; มันง่ายกว่าที่จะใช้.*
และส่งค่าสองเท่าและจำนวนตำแหน่งทศนิยมเป็นอาร์กิวเมนต์ไปยังการsprintf
โทรเดียวกัน
#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
ก่อนอื่นให้ฉันพิสูจน์เหตุผลของฉันในการเพิ่มอีกคำตอบสำหรับคำถามนี้ ในโลกอุดมคติการปัดเศษไม่ใช่เรื่องใหญ่จริงๆ อย่างไรก็ตามในระบบจริงคุณอาจต้องต่อสู้กับปัญหาต่าง ๆ ที่อาจทำให้เกิดการปัดเศษซึ่งอาจไม่ใช่สิ่งที่คุณคาดหวัง ตัวอย่างเช่นคุณอาจทำการคำนวณทางการเงินซึ่งผลลัพธ์สุดท้ายจะถูกปัดเศษและแสดงให้ผู้ใช้เห็นเป็นทศนิยม 2 ตำแหน่ง ค่าเดียวกันเหล่านี้จะถูกเก็บไว้พร้อมกับความแม่นยำคงที่ในฐานข้อมูลที่อาจมีทศนิยมมากกว่า 2 ตำแหน่ง (ด้วยเหตุผลต่าง ๆ ไม่มีตำแหน่งที่เหมาะสมที่สุดที่จะเก็บ ... ขึ้นอยู่กับสถานการณ์ที่เฉพาะเจาะจงแต่ละระบบต้องรองรับเช่นรายการเล็ก ๆ เป็นเศษส่วนของเงินต่อหน่วย) และการคำนวณจุดลอยตัวดำเนินการกับค่าที่ผลลัพธ์เป็นบวก / ลบ epsilon ฉันเผชิญกับปัญหาเหล่านี้และพัฒนากลยุทธ์ของตัวเองในช่วงหลายปีที่ผ่านมา ฉันจะไม่อ้างว่าฉันได้เผชิญกับทุกสถานการณ์หรือมีคำตอบที่ดีที่สุด แต่ด้านล่างเป็นตัวอย่างของวิธีการของฉันที่เอาชนะปัญหาเหล่านี้ได้:
สมมติว่าตำแหน่งทศนิยม 6 ตำแหน่งนั้นมีความแม่นยำเพียงพอสำหรับการคำนวณแบบลอยตัว / เป็นสองเท่า (การตัดสินใจตามอำเภอใจสำหรับการใช้งานเฉพาะ) โดยใช้ฟังก์ชัน / วิธีการปัดเศษต่อไปนี้:
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;
}
}
การปัดเศษทศนิยม 2 ตำแหน่งสำหรับการนำเสนอผลลัพธ์สามารถทำได้ดังนี้:
double val;
// ...perform calculations on val
String(Round(Round(Round(val,8),6),2));
สำหรับval = 6.825
ผลลัพธ์คือ6.83
ตามที่คาดไว้
สำหรับผลคือval = 6.824999
6.82
นี่คือสมมติฐานที่ว่าการคำนวณส่งผลให้ตรง6.824999
และทศนิยมตำแหน่งที่ 7 เป็นศูนย์
สำหรับผลคือval = 6.8249999
6.83
ตำแหน่งทศนิยมที่ 7 ที่อยู่9
ในกรณีนี้ทำให้Round(val,6)
ฟังก์ชันให้ผลลัพธ์ที่คาดหวัง สำหรับกรณีนี้อาจมีการติดตามจำนวนเท่าใดก็ได้9
s
สำหรับผลคือval = 6.824999499999
6.83
การปัดเศษทศนิยมที่ 8 เป็นขั้นตอนแรกคือRound(val,8)
การจัดการกรณีที่น่ารังเกียจหนึ่งซึ่งผลการคำนวณจุดลอยที่คำนวณได้จะถูกคำนวณ6.8249995
แต่จะแสดงภายในเป็น6.824999499999...
แต่เป็นตัวแทนภายในเป็น
สุดท้ายตัวอย่างจากคำถาม ... val = 37.777779
ส่งผลให้37.78
ผลในการ
วิธีการนี้อาจเป็นแนวทางทั่วไปเพิ่มเติม:
double val;
// ...perform calculations on val
String(Round(Round(Round(val,N+2),N),2));
โดยที่ N คือความแม่นยำที่จะคงไว้สำหรับการคำนวณระดับกลางทั้งหมดในการลอย / ดับเบิล สิ่งนี้ใช้ได้กับค่าลบเช่นกัน ฉันไม่รู้ว่าวิธีนี้ถูกต้องทางคณิตศาสตร์สำหรับความเป็นไปได้ทั้งหมดหรือไม่
รหัส C ง่าย ๆ สำหรับการปัดเศษตัวเลข:
float n = 3.56;
printf("%.f", n);
ผลลัพธ์นี้:
4
... หรือคุณสามารถทำได้ในแบบที่ไม่มีห้องสมุด:
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;
แน่นอนว่าถ้าคุณต้องการลบข้อมูลพิเศษจากหมายเลขนั้น
ฟังก์ชั่นนี้ใช้จำนวนและความแม่นยำและส่งกลับตัวเลขปัดเศษ
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;
}
มันแปลงจำนวนจุดลอยตัวเป็น int โดยซ้ายขยับจุดและตรวจสอบเงื่อนไขที่มากกว่าห้า
float
(และdouble
) ไม่ใช่ทศนิยมทศนิยม - เป็นทศนิยมทศนิยมฐานสองดังนั้นการปัดเศษเป็นทศนิยมจึงไม่มีความหมาย อย่างไรก็ตามคุณสามารถปัดเศษผลลัพธ์ได้