ฉันจะ จำกัด ค่าทศนิยมเพียงสองตำแหน่งหลังจากจุดทศนิยมใน C ได้อย่างไร


215

ฉันจะปัดเศษทศนิยม (เช่น 37.777779) เป็นทศนิยมสองตำแหน่ง (37.78) ใน C ได้อย่างไร


15
คุณไม่สามารถปัดเศษตัวเลขได้อย่างถูกต้องเนื่องจากfloat(และdouble) ไม่ใช่ทศนิยมทศนิยม - เป็นทศนิยมทศนิยมฐานสองดังนั้นการปัดเศษเป็นทศนิยมจึงไม่มีความหมาย อย่างไรก็ตามคุณสามารถปัดเศษผลลัพธ์ได้
Pavel Minaev

63
มันไม่ได้ไร้ความหมาย มันไม่แน่นอน มีความแตกต่างค่อนข้าง
บรูคส์โมเสส

2
การปัดเศษแบบไหนที่คุณคาดหวัง Half-up หรือ Rounding ไปยังที่ใกล้ที่สุด
Truthseeker Rangwan

คำตอบ:


407

หากคุณต้องการปัดเศษตัวเลขเพื่อเอาท์พุท"%.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 */

โปรดสังเกตว่ามีกฎการปัดเศษที่แตกต่างกันสามข้อที่คุณอาจต้องการเลือก: ปัดเศษลง (เช่นตัดหลังทศนิยมสองตำแหน่ง) ปัดเศษให้ใกล้ที่สุดและปัดเศษขึ้น โดยปกติแล้วคุณต้องการรอบที่ใกล้ที่สุด

ในขณะที่คนอื่นหลายคนชี้ให้เห็นเนื่องจากองค์ประกอบของการเป็นตัวแทนจุดลอยตัวค่าที่ปัดเศษเหล่านี้อาจไม่ตรงกับค่าทศนิยม "ชัดเจน" แต่พวกเขาจะอยู่ใกล้มาก

สำหรับมาก (มาก!) ข้อมูลเพิ่มเติมเกี่ยวกับการปัดเศษและโดยเฉพาะอย่างยิ่งในการผูกทำลายกฎระเบียบสำหรับการปัดเศษที่ใกล้ที่สุดให้ดูบทความวิกิพีเดียปัดเศษ


4
มันสามารถแก้ไขเพื่อรองรับการปัดเศษเพื่อความแม่นยำโดยพลการหรือไม่?

1
@slater เมื่อคุณพูดว่า 'ความแม่นยำตามอำเภอใจ' คุณจะถามเกี่ยวกับการปัดเศษเช่นสามแทนทศนิยมสองตำแหน่งหรือการใช้ไลบรารีที่ใช้ค่าทศนิยมแม่นยำไม่ จำกัด ? หากอดีตทำในสิ่งที่ฉันหวังว่าจะมีการปรับค่าคงที่ 100 อย่างชัดเจน มิฉะนั้นทำการคำนวณแบบเดียวกันกับที่แสดงไว้ด้านบนเพียงแค่ใช้ไลบรารี่ที่มีความแม่นยำหลากหลายที่คุณใช้
Dale Hagglund

2
@DaleHagglung อดีตขอบคุณ การปรับเปลี่ยนเพื่อแทนที่ 100 ด้วย pow (10, (int) ที่ต้องการความแม่นยำ) หรือไม่

3
อ๋อ ในการปัดเศษทศนิยมหลัง k ให้ใช้ตัวคูณสเกลเป็น 10 ^ k นี่ควรจะง่ายจริง ๆ ดูว่าคุณเขียนค่าทศนิยมด้วยมือและเล่นรอบกับทวีคูณของ 10 สมมติว่าคุณกำลังทำงานกับค่า 1.23456789 และต้องการปัดเศษทศนิยม 3 ตำแหน่ง การดำเนินงานที่มีให้คุณเป็นรอบจำนวนเต็ม ดังนั้นคุณจะย้ายตำแหน่งทศนิยมสามตำแหน่งแรกเพื่อให้พวกเขาออกจากจุดทศนิยมได้อย่างไร ฉันหวังว่ามันชัดเจนว่าคุณคูณด้วย 10 ^ 3 ตอนนี้คุณสามารถปัดเศษค่านั้นเป็นจำนวนเต็ม ถัดไปคุณใส่ตัวเลขลำดับต่ำสามหลักด้วยการหารด้วย 10 ^ 3
Dale Hagglund

1
ฉันจะทำให้งานนี้ด้วยdoublesหรือไม่ ดูเหมือนจะไม่ทำงานที่ฉันต้องการ :( (ใช้floorและceil)
คุณไม่มีใครใน

87

การใช้% .2fใน printf มันพิมพ์ทศนิยม 2 ตำแหน่งเท่านั้น

ตัวอย่าง:

printf("%.2f", 37.777779);

เอาท์พุท:

37.77

วิธีนี้ดีกว่าเพราะไม่มีการสูญเสียความแม่นยำ
อัลเบิร์ต

2
@albert นี่ยังมีข้อได้เปรียบของการไม่สูญเสียfloatช่วงที่val * 100สามารถล้นได้
chux - Reinstate Monica

42

สมมติว่าคุณกำลังพูดถึงรอบค่าสำหรับการพิมพ์แล้ว คำตอบของAndrew ColesonและAraKนั้นถูกต้อง:

printf("%.2f", 37.777779);

แต่โปรดทราบว่าหากคุณต้องการปัดเศษตัวเลขให้เป็น 37.78 สำหรับการใช้ภายใน (เช่นเพื่อเปรียบเทียบกับค่าอื่น) นี่ไม่ใช่ความคิดที่ดีเนื่องจากวิธีการทำงานของตัวเลขจุดลอยตัว: โดยปกติคุณจะไม่ ต้องการเปรียบเทียบความเท่าเทียมกันสำหรับ floating point แทนใช้ค่าเป้าหมาย +/- a sigma value หรือเข้ารหัสตัวเลขเป็นสตริงด้วยความแม่นยำที่รู้จักและเปรียบเทียบ

ดูลิงก์ในคำตอบของ Greg Hewgill สำหรับคำถามที่เกี่ยวข้องซึ่งครอบคลุมถึงสาเหตุที่คุณไม่ควรใช้ทศนิยมสำหรับการคำนวณทางการเงิน


1
เพิ่มขึ้นสำหรับการจัดการกับสิ่งที่อาจเป็นคำถามที่อยู่เบื้องหลังคำถาม (หรือคำถามที่ควรจะอยู่ข้างหลังคำถาม!) นั่นเป็นจุดที่ค่อนข้างสำคัญ
บรู๊คโมเสส

ที่จริงแล้ว 37.78 สามารถแสดงได้อย่างแม่นยำตามจุดลอย Float มี 11 ถึง 12 หลักสำหรับการตกตะกอน นั่นควรจะเพียงพอที่จะจัดการกับ 3778 377.8 หรือทศนิยม 4 หลักทุกชนิด
ไม่ระบุชื่อสีขาว

@HaryantoCiu ใช่แล้วพอฉันได้แก้ไขคำตอบของฉันเล็กน้อย
John Carter

ความแม่นยำแบบไดนามิก:printf("%.*f", (int)precision, (double)number);
Minhas Kamal

24

เกี่ยวกับสิ่งนี้:

float value = 37.777779;
float rounded = ((int)(value * 100 + .5) / 100.0);

4
-1: a) วิธีนี้ใช้ไม่ได้กับจำนวนลบ (โอเคตัวอย่างเป็นบวก แต่ยังคงอยู่) b) คุณไม่ต้องพูดถึงว่ามันเป็นไปไม่ได้ที่จะเก็บค่าทศนิยมที่แน่นอนในทุ่น
John Carter

32
@therefromhere: (a) คุณถูก (ข) นี่คืออะไร? แบบทดสอบมัธยมปลาย?
Daniil

1
ทำไมคุณเพิ่ม 0.5?
มูฮัมหมัด tayyab

1
จำเป็นต้องปฏิบัติตามกฎการปัดเศษ
Daniil

1
กฎการปัดเศษในบริบทของความคิดเห็น @Daniil เป็นรอบที่ใกล้ที่สุด
Shmil The Cat

20
printf("%.2f", 37.777779);

หากคุณต้องการเขียนถึง C-string:

char number[24]; // dummy size, you should take care of the size!
sprintf(number, "%.2f", 37.777779);

@Sinan: ทำไมการแก้ไข? @AraK: ไม่คุณควรดูแลขนาด :) ใช้ snprintf ()
aib

1
@aib: ฉันเดาเพราะ / ** / C มีความคิดเห็นรูปแบบและเป็นคำถามที่ติดแท็กสำหรับ C
ไมเคิลฮาเรน

5
C89 อนุญาตเฉพาะ / ** / - สไตล์ C99 แนะนำการสนับสนุนสำหรับ // - สไตล์ ใช้คอมไพเลอร์ lame / old (หรือบังคับโหมด C89) และคุณจะไม่สามารถใช้ // - style ได้ ต้องบอกว่ามันเป็นปี 2009 มาลองพิจารณาพวกเขาทั้งสไตล์ C และ C ++
Andrew Coleson

11

ไม่มีวิธีการปัด a floatไปยังอีกfloatเนื่องจากการปัดเศษfloatอาจไม่สามารถแทนได้ (ข้อ จำกัด ของตัวเลขทศนิยม) ตัวอย่างเช่นสมมติว่าคุณปัด 37.777779 ถึง 37.78 แต่จำนวนที่สามารถแทนได้ที่ใกล้ที่สุดคือ 37.781

อย่างไรก็ตามคุณสามารถ "ปัดเศษ" a floatโดยใช้ฟังก์ชันสตริงรูปแบบ


3
นี่ไม่แตกต่างจากการพูดว่า "ไม่มีทางที่จะหารสองโฟลตและรับโฟลตได้เนื่องจากผลลัพธ์ที่แบ่งออกอาจไม่สามารถแทนได้" ซึ่งอาจเป็นจริงอย่างแน่นอน แต่ไม่เกี่ยวข้องกัน ลอยอยู่เสมอไม่แน่นอนแม้กระทั่งบางสิ่งบางอย่างที่เป็นพื้นฐานนอกจากนี้; สมมติฐานคือสิ่งที่คุณได้รับเสมอคือ "ทุ่นที่ใกล้เคียงที่สุดกับคำตอบที่ถูกต้อง"
บรู๊คโมเสส

สิ่งที่ฉันหมายถึงคือคุณไม่สามารถปัดเศษfloatทศนิยมa ถึง n แล้วคาดว่าผลลัพธ์จะมีทศนิยมสิบตำแหน่งเสมอ คุณจะยังคงได้รับfloatไม่ใช่เพียงที่คุณคาดหวัง
Andrew Keeton

9

นอกจากนี้หากคุณใช้ 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);

7

คุณยังสามารถใช้:

float ceilf(float x); // don't forget #include <math.h> and link with -lm.

ตัวอย่าง:

float valueToRound = 37.777779;
float roundedValue = ceilf(valueToRound * 100) / 100;

ตัดทอนที่จุดทศนิยม (เช่นจะสร้าง 37) และเขาต้องการปัดเศษสองตำแหน่งหลังจากจุดทศนิยม
Pavel Minaev

การปัดเศษสองตำแหน่งหลังจากจุดทศนิยมเป็นความเปลี่ยนแปลงเล็กน้อย (แต่ควรจะกล่าวถึงในคำตอบ ZeroCool ต้องการเพิ่มการแก้ไขหรือไม่): float floatingValue = ceilf (valueToRound * 100.0) / 100.0;
บรูคส์โมเสส

เข้าสู่สถานะของการนอนหลับ :)
ZeroCool

ทำไมโซลูชันนี้ถึงไม่เป็นที่นิยมมากขึ้น? วิธีนี้ใช้ได้ผลกับรหัสน้อยที่สุด มีข้อแม้อยู่บ้างไหม?
Andy

7

ใน 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 ตัวในฟังก์ชั่นนั้น แต่ฉันทิ้งไว้ที่นั่นเพื่อให้คุณเห็นตรรกะ อาจจะมีวิธีแก้ไขที่ง่ายกว่า แต่วิธีนี้ใช้ได้ผลดีสำหรับฉัน - โดยเฉพาะอย่างยิ่งเนื่องจากช่วยให้ฉันสามารถปรับจำนวนตัวเลขหลังตำแหน่งทศนิยมตามที่ฉันต้องการ


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 อีกครั้ง - มีข้อบกพร่องสองวิธี:

  • สำหรับบางค่ามันจะปัดไปในทิศทางที่ไม่ถูกต้องเนื่องจากการคูณด้วย 100 จะเปลี่ยนทศนิยมหลักที่กำหนดทิศทางการปัดเศษจาก 4 เป็น 5 หรือในทางกลับกันเนื่องจากความไม่แน่นอนของจำนวนจุดลอยตัว
  • สำหรับค่าบางค่าการคูณแล้วหารด้วย 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


+1 สำหรับตัวอย่างที่เป็นรูปธรรมของสิ่งที่ผิดพลาดกับคำตอบของฉันและสิ่งที่คล้ายกับมันขอบคุณความแปลกประหลาดของจุดลอยตัว IEEE และเป็นทางเลือกที่ตรงไปตรงมา ฉันรู้ว่าอุปกรณ์ต่อพ่วงค่อนข้างใช้เวลานานมากในการพิมพ์และเพื่อน ๆ ก็ปลอดภัยสำหรับจุดลอยตัว ฉันเดาว่างานที่ทำเสร็จแล้วอาจปรากฏที่นี่
Dale Hagglund

อะแฮ่ม ... ขออภัยสำหรับคำว่าสลัดใกล้จะถึงจุดสิ้นสุดซึ่งตอนนี้มันก็สายเกินไปที่จะแก้ไข สิ่งที่ฉันหมายถึงคือ "... ความพยายามอย่างมากใส่ลงใน printf และเพื่อน ๆ เพื่อให้พวกเขาปลอดภัย ... "
Dale Hagglund

4

ใช้ 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. ค่าที่ไม่ถูกต้องนี้อาจถูกมองว่าถูกต้องขึ้นอยู่กับเป้าหมายในการเขียนโค้ด


4

ฉันสร้างมาโครนี้เพื่อปัดเศษจำนวนลอย เพิ่มไว้ในส่วนหัว / ไฟล์ของคุณ

#define ROUNDF(f, c) (((float)((int)((f) * (c))) / (c)))

นี่คือตัวอย่าง:

float x = ROUNDF(3.141592, 100)

x เท่ากับ 3.14 :)


สิ่งนี้ถูกตัดทอน แต่คำถามขอให้ปัดเศษ นอกจากนี้ยังขึ้นอยู่กับข้อผิดพลาดในการปัดเศษในการดำเนินการจุดลอยตัว
Eric Postpischil

3
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

-1 ด้วยเหตุผลสี่ประการ: 1) การขาดคำอธิบาย 2) ช่องโหว่ในการบัฟเฟอร์ล้น - สิ่งนี้จะล้นและดังนั้นจึงอาจผิดพลาดได้ถ้าdvalมีขนาดใหญ่ 3) แปลกif/ elseบล็อกที่คุณทำสิ่งเดียวกันในแต่ละสาขา และ 4) การใช้ที่ซับซ้อนของsprintfการสร้างตัวระบุรูปแบบสำหรับการsprintfโทรครั้งที่สอง; มันง่ายกว่าที่จะใช้.*และส่งค่าสองเท่าและจำนวนตำแหน่งทศนิยมเป็นอาร์กิวเมนต์ไปยังการsprintfโทรเดียวกัน
Mark Amery

3

นิยามของรหัส:

#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

0

ก่อนอื่นให้ฉันพิสูจน์เหตุผลของฉันในการเพิ่มอีกคำตอบสำหรับคำถามนี้ ในโลกอุดมคติการปัดเศษไม่ใช่เรื่องใหญ่จริงๆ อย่างไรก็ตามในระบบจริงคุณอาจต้องต่อสู้กับปัญหาต่าง ๆ ที่อาจทำให้เกิดการปัดเศษซึ่งอาจไม่ใช่สิ่งที่คุณคาดหวัง ตัวอย่างเช่นคุณอาจทำการคำนวณทางการเงินซึ่งผลลัพธ์สุดท้ายจะถูกปัดเศษและแสดงให้ผู้ใช้เห็นเป็นทศนิยม 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 คือความแม่นยำที่จะคงไว้สำหรับการคำนวณระดับกลางทั้งหมดในการลอย / ดับเบิล สิ่งนี้ใช้ได้กับค่าลบเช่นกัน ฉันไม่รู้ว่าวิธีนี้ถูกต้องทางคณิตศาสตร์สำหรับความเป็นไปได้ทั้งหมดหรือไม่



-1

... หรือคุณสามารถทำได้ในแบบที่ไม่มีห้องสมุด:

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;

แน่นอนว่าถ้าคุณต้องการลบข้อมูลพิเศษจากหมายเลขนั้น


-2

ฟังก์ชั่นนี้ใช้จำนวนและความแม่นยำและส่งกลับตัวเลขปัดเศษ

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 โดยซ้ายขยับจุดและตรวจสอบเงื่อนไขที่มากกว่าห้า

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.