Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::setprecision sets the number of significant figures. How do I use iomanip to set the precision?

Tags:

c++

iomanip

I have always found iomanip confusing and counter intuitive. I need help.

A quick internet search finds (https://www.vedantu.com/maths/precision) "We thus consider precision as the maximum number of significant digits after the decimal point in a decimal number" (the emphasis is mine). That matches my understanding too. However I wrote a test program and:

stm << std::setprecision(3) << 5.12345678;
std::cout << "5.12345678: " << stm.str() << std::endl;
stm.str("");

stm << std::setprecision(3) << 25.12345678;
std::cout << "25.12345678: " << stm.str() << std::endl;
stm.str("");

stm << std::setprecision(3) << 5.1;
std::cout << "5.1: " << stm.str() << std::endl;
stm.str("");

outputs:

5.12345678: 5.12
25.12345678: 25.1
5.1: 5.1

If the precision is 3 then the output should be:

5.12345678: 5.123
25.12345678: 25.123
5.1: 5.1

Clearly the C++ standard has a different interpretation of the meaning of "precision" as relates to floating point numbers.

If I do:

stm.setf(std::ios::fixed, std::ios::floatfield);

then the first two values are formatted correctly, but the last comes out as 5.100.

How do I set the precision without padding?

like image 255
AlastairG Avatar asked Mar 18 '21 12:03

AlastairG


People also ask

Which function is used to set the precision?

The setprecision is a manipulator function in C++ which is used to sets the decimal precision of floating-point values on output operations. This setprecision is the built-in function defined in <iomanip> header file in C++.

How do you set the precision of a float variable in C++?

You can't set precision of a float or double variable. Only for the input/output of such a variable. But you shouldn't be using float or double for monetary values to begin with, due to the fact that floating-point values/math is inprecise.

How do you control significant figures in C++?

C++ has a library called iomanip which provides us a function named setprecision() using which we can change the number of significant digits.


2 Answers

You can try using this workaround:

decltype(std::setprecision(1)) setp(double number, int p) {
    int e = static_cast<int>(std::abs(number));
    e = e != 0? static_cast<int>(std::log10(e)) + 1 + p : p;
    while(number != 0.0 && static_cast<int>(number*=10) == 0 && e > 1) 
        e--; // for numbers like 0.001: those zeros are not treated as digits by setprecision.
    return std::setprecision(e);
}

And then:

auto v = 5.12345678;
stm << setp(v, 3) << v;

Another more verbose and elegant solution is to create a struct like this:

struct __setp {
    double number;
    bool fixed = false;
    int prec;
};

std::ostream& operator<<(std::ostream& os, const __setp& obj)
{
    if(obj.fixed)
        os << std::fixed;
    else os << std::defaultfloat;
    os.precision(obj.prec);
    os << obj.number; // comment this if you do not want to print immediately the number
    return os;
}

__setp setp(double number, int p) {
     __setp setter;
     setter.number = number;
     
    int e = static_cast<int>(std::abs(number));
    e = e != 0? static_cast<int>(std::log10(e)) + 1 + p : p;
    while(number != 0.0 && static_cast<int>(number*=10) == 0)
        e--; // for numbers like 0.001: those zeros are not treated as digits by setprecision.

    if(e <= 0) {
        setter.fixed = true;
        setter.prec = 1;
    } else
        setter.prec = e;
    return setter;
}

Using it like this:

auto v = 5.12345678;
stm << setp(v, 3);
like image 69
Doch88 Avatar answered Oct 03 '22 00:10

Doch88


I don't think you can do it nicely. There are two candidate formats: defaultfloat and fixed. For the former, "precision" is the maximum number of digits, where both sides of the decimal separator count. For the latter "precision" is the exact number of digits after the decimal separator.

So your solution, I think, is to use fixed format and then manually clear trailing zeros:

#include <iostream>
#include <iomanip>
#include <sstream>

void print(const double number)
{
    std::ostringstream stream;
    stream << std::fixed << std::setprecision(3) << number;
    auto string=stream.str();
    while(string.back()=='0')
        string.pop_back();
    if(string.back()=='.') // in case number is integral; beware of localization issues
        string.pop_back();
    std::cout << string << "\n";
}

int main()
{
    print(5.12345678);
    print(25.12345678);
    print(5.1);
}
like image 22
Ruslan Avatar answered Oct 02 '22 22:10

Ruslan