Home > database >  round error of C when using fixed and setprecision in stringstream
round error of C when using fixed and setprecision in stringstream

Time:11-14

I wrote a program in C and I think the result of it should be 12.3 and 12.2. But the final result is 12.2 and 12.2. Obviously, it is not rounding rightly. But why? Thanking you very much :)

#include <iostream>
#include <iomanip>

using namespace std;

string dtos(double num) {
    stringstream ss;
    ss << fixed << setprecision(1) << num;
    return ss.str();
}

int main() {
    double num1 = 12.25;
    double num2 = 12.24;
    string str1 = dtos(num1);
    string str2 = dtos(num2);
    cout << str1 << ' ' << str2 << endl;
    return 0;
}

I also tried the way that num1 = 12.05 and num2 = 12.04 and get the desired result as 12.1 and 12.0.

CodePudding user response:

Before you ask "why double num1 = 12.25; is not successfully rounded up to 12.3", you have to be aware that double/float cannot represent all non-decimal numbers, and comparing double/float is not that "accurate"; see https://en.wikipedia.org/wiki/IEEE_754

Regarding your question, it might because that num1 does not contain 12.25, but something smaller than that (something like, 12.2499999999...) and the comparision (num1 >= 12.5) is not necessary true. If you set it to 12.26 or 12.51, you will see the expected output.

CodePudding user response:

I inspected the feature of each round function. As a result, basically these functions return nearest integer from the value, but if the value is in the position of halfway between two integers, there are the type of behaviors below for calculating.

  • rounding type
    • NI: nearest integer far from zero. (e.g. 2.5 -> 3)
    • NE: nearest even integer from the value. (e.g. 2.5 -> 2)
  • halfway type
    • Exactly: take floating point error into account. (e.g. 2.500002 is not halfway)
    • Not Exactly: ignore floating point error. (e.g. 2.500002 is regarded as 2.5)

That's why each function can be classified to several types below.

  1. setprecision

    • rounding type: NE
    • halfway type: Exactly
  2. rint

    • rounding type: NE
    • halfway type: Not Exactly
  3. round

    • rounding type: NI
    • halfway type: Not Exactly
#include <iostream>
#include <cfenv>
#include <iomanip>
#include <math.h>
using namespace std;

int main() {
    fesetround(FE_TONEAREST);
    //fesetround(FE_DOWNWARD);
    //fesetround(FE_UPWARD);
    printf("round type=%d\n\n", fegetround());

    auto f = [&](auto v) {
        printf("%-14s %.30f\n", "orig val", v);
        cout << fixed << "setprecision" << setw(7) << setprecision(1) << v << endl;
        printf("%-14s %.30f\n", "rint", rint((v * 10)) / 10);
        printf("%-14s %.30f\n", "round", round((v * 10)) / 10);
        cout << "-----------------" << endl;
    };
    f(12.05);
    f(12.25);
    f(12.35);
    f(12.75);
}

output is

round type=0

orig val       12.050000000000000710542735760100
setprecision   12.1
rint           12.000000000000000000000000000000
round          12.099999999999999644728632119950
-----------------
orig val       12.250000000000000000000000000000
setprecision   12.2
rint           12.199999999999999289457264239900
round          12.300000000000000710542735760100
-----------------
orig val       12.349999999999999644728632119950
setprecision   12.3
rint           12.400000000000000355271367880050
round          12.400000000000000355271367880050
-----------------
orig val       12.750000000000000000000000000000
setprecision   12.8
rint           12.800000000000000710542735760100
round          12.800000000000000710542735760100
-----------------
  • Related