I want to format a floating point value to n significant digits but never using scientific notation (even if it would be shorter).
The format specification %f doesn't deal in significant digits, and %g will sometimes give me scientific notation (which is inappropriate for my use).
I want values in the form "123", "12.3", "1.23" or "0.000000123".
Is there an elegant way to do this using C++ or boost?
The best way I know (and use it in my own code) is
#include <string>
#include <math.h>
#include <sstream>
#include <iomanip>
int round(double number)
{
return (number >= 0) ? (int)(number + 0.5) : (int)(number - 0.5);
}
std::string format(double f, int n)
{
if (f == 0) {
return "0";
}
int d = (int)::ceil(::log10(f < 0 ? -f : f)); /*digits before decimal point*/
double order = ::pow(10., n - d);
std::stringstream ss;
ss << std::fixed << std::setprecision(std::max(n - d, 0)) << round(f * order) / order;
return ss.str();
}
c++11 has std::round so you won't need my version of with a new compiler.
The trick I'm exploiting here is to get the precision you want by taking the base 10 log to count the number of digits before the decimal and subtracting this from the precision you want.
It satisfies #Mats Petersson's requirement too, so will work in all cases.
The bit I don't like is the initial check for zero (so the log function doesn't blow up). Suggestions for improvement / direct editing of this answer most welcome.
std::fixed and std::setprecision (and <iomanip> in general) are your friends.
std::cout << 0.000000123 << '\n';
prints 1.23e-07 and
std::cout << std::setprecision(15) << std::fixed << 0.000000123 << '\n';
prints 0.000000123000000
Just remember that floating-point numbers have limited precision, so
std::cout << std::fixed << 123456789012345678901234567890.0 << '\n';
will print 123456789012345677877719597056.000000 (probably not what you want)
I think you'll have to remove trailing zeros by yourself :
string trimString(string str)
{
string::size_type s;
for(s=str.length()-1; s>0; --s)
{
if(str[s] == '0') str.erase(s,1);
else break;
}
if(str[s] == '.') str.erase(s,1);
return str;
}
Usage :
double num = 0.000000123;
stringstream ss;
ss << num;
ss.str("");
ss << std::setprecision(15) << std::fixed << num; // outputs 0.000000123000000
string str;
ss >> str;
str = trimString(str);
cout << str << endl; // outputs 0.000000123
Put together :
string format(int prec, double d) {
stringstream ss;
ss << d;
ss.str("");
ss << std::setprecision(prec) << std::fixed << d;
string str;
ss >> str;
string::size_type s;
for(s=str.length() - 1; s > 0; --s)
{
if(str[s] == '0') str.erase(s,1);
else break;
}
if(str[s] == '.') str.erase(s,1);
return str;
}
Usage :
double num = 0.000000123;
cout << format(15, num) << std::endl;
If someone knows a better way ...
Related
Im trying to get a float value from a file.txt into a string. When I output that value with std::stof(str) it gets rounded. Example, in the text file there's "101471.71", whet i use the std::stof(str) it returns "101472", how to I avoid this?
Here's a part of that code (some parts are in spanish, sorry :p):
double CaptureLine(std::string filepath, int fileline, int linesize)
{
std::fstream file;
std::string n_str, num_n;
int current_line = 0, n_size, filesize = FileSize(filepath);
char ch_n;
double n_float = 0.0;
int n_line = filesize - fileline;
file.open("registros.txt");
if (file.is_open()) {
while (!file.eof()) {
current_line++;
std::getline(file, n_str);
if (current_line == n_line) break;
}
if (current_line < n_line) {
std::cout << "Error" << std::endl;
return 1;
}
file.close();
}
n_size = n_str.length();
for (int i = linesize; i < n_size; i++) {
ch_n = n_str.at(i);
num_n.push_back(ch_n);
}
std::cout << ">>" << num_n << "<<\n";
n_float = std::stof(num_n); //Here's the error
return n_float;
}
The issue probably isn't with std::stof, but is probably with the default precision of 6 in std::cout. You can use std::setprecision to increase that precision and capture all of your digits.
Here's a program that demonstrates:
#include <iostream>
#include <iomanip>
#include <string>
int main() {
std::cout << 101471.71f << "\n";
std::cout << std::stof("101471.71") << "\n";
std::cout << std::setprecision(8) << 101471.71f << "\n";
std::cout << std::stof("101471.71") << "\n";
return 0;
}
Outputs:
101472
101472
101471.71
101471.71
Be aware that std::setprecision sticks to the std::cout stream after it's called. Notice how the above example calls it exactly once but its effect sticks around.
Live On Coliru
FormatFloat
I try to implement one conversion of Golang strconv.FormatFloat() in C++.
#include <sstream>
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
std::string convert_str(double d)
{
std::stringstream ss;
if (d >= 0.0001)
{
ss << std::fixed << std::setprecision(4); // I know the precision, so this is fine
ss << d;
return ss.str();
}
else
{
ss << std::scientific;
ss << d;
return ss.str();
}
}
int main()
{
std::cout << convert_str(0.002) << std::endl; // 0.0020
std::cout << convert_str(0.00001234560000) << std::endl; // 1.234560e-05
std::cout << convert_str(0.000012) << std::endl; // 1.200000e-05
return 0;
}
Output:
0.0020
1.234560e-05 // should be 1.23456e-05
1.200000e-05 // should be 1.2e-05
Question> How can I setup the output modifier so that the trailing zero doesn't show up?
strconv.FormatFloat(num, 'e', -1, 64)
The special precision value (-1) is used for the smallest number of
digits necessary such that ParseFloat() will return f exactly.
At the risk of being heavily downvoted criticised for posting a C answer to a C++ question ... you can use the %lg format specifier in a call to sprintf.
From cpprefernce:
Unless alternative representation is requested the trailing zeros are
removed, also the decimal point character is removed if no fractional
part is left.
So, if you only want to remove the trailing zeros when using scientific notation, you can change your convert_str function to something like the following:
std::string convert_str(double d)
{
if (d >= 0.0001) {
std::stringstream ss;
ss << std::fixed << std::setprecision(4); // I know the precision, so this is fine
ss << d;
return ss.str();
}
else {
char cb[64];
sprintf(cb, "%lg", d);
return cb;
}
}
For the three test cases in your code, this will give:
0.0020
1.23456e-05
1.2e-05
From C++20 and later, the std::format class may offer a more modern alternative; however, I'm not (yet) fully "up to speed" with that, so I cannot present a solution using it. Others may want to do so.
Yes, std::scientific don't remove trailing zeros from scientific notation. The good news, for your specific case, is that cout already format values below 0.0001 using scientific notation, and removing trailing zeros. So you can let your code like this:
#include <sstream>
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
std::string convert_str(double d)
{
std::stringstream ss;
if (d >= 0.0001)
{
ss << std::fixed << std::setprecision(4); // I know the precision, so this is fine
ss << d;
return ss.str();
}
else
{
ss << d;
return ss.str();
}
}
int main()
{
std::cout << convert_str(0.002) << std::endl; // 0.0020
std::cout << convert_str(0.00001234560000) << std::endl; // 1.23456e-05
std::cout << convert_str(0.000012) << std::endl; // 1.2e-05
return 0;
}
The wanted output can be generated with a combination of the std::setprecision and std::defaultfloat manipulators:
std::cout << std::setprecision(16) << std::defaultfloat
<< 0.002 << '\n'
<< 0.00001234560000 << '\n'
<< 0.000012 << '\n';
Live at: https://godbolt.org/z/67fWa1seo
I want to print to screen some numbers with at most 4 digits after decimal point using iomanip.
I've learned that in default mode setprecision counts not only the digits after decimal point but also the digits in the integer part. This code
#include <iostream>
#include <iomanip>
int main () {
double numbers[] = {3.141516, 1.01, 200.78901, 0.12345};
int len = sizeof(numbers) / sizeof(numbers[0]);
std::cout << std::setprecision(4);
for (int i = 0; i < len; ++i) {
std::cout << numbers[i] << '\n';
}
return 0;
}
outputs:
3.142
1.01
200.8
0.1235
But what I want is: (at most 4 digits after decimal point without trailing zeros)
3.1415
1.01
200.789
0.1235
Is iomanip capable of doing this? Without using other tricks (like round)?
EDIT
It seems that I haven't made it clear enough. My question is iomanip specific
All I want to know is whether iomanip is capable of doing things I've described because iomanip is said to be the standard library for input/output manipulators. The posted question is
Is iomanip capable of doing this?
It's more like "is it supported" rather than "give me any solution".
I have searched it again, looked up iomanip references hoping for a clean and compact way to format floating numbers for at most n digits, using unnecessary libraries as little as possible.
And there seems to be no standard way to achieve this.
One (ugly) option to obtain OP's desired output is to represent the number with the wanted maximum precision and then just remove the unwanted zeroes:
#include <iostream>
#include <iomanip>
#include <sstream>
#include <vector>
int main()
{
std::vector<double> numbers {
3.141516, 1.01, 200.78901, 0.12345, 9.99999
};
for (auto x : numbers)
{
// "print" the number
std::stringstream ss;
ss << std::fixed << std::setprecision(4) << x;
// remove the unwanted zeroes
std::string result{ss.str()};
while (result.back() == '0')
result.pop_back();
// remove the separator if needed
if (result.back() == '.')
result.pop_back();
std::cout << result << '\n';
}
std::cout << "\nJustified:\n";
for (auto x : numbers)
{
// "print" the number
std::stringstream ss;
ss << std::fixed << std::setprecision(4) << std::setw(15) << x;
// remove the unwanted zeroes
std::string result{ss.str()};
auto it = result.rbegin();
while (*it == '0')
*it++ = ' ';
// remove the separator if needed
if (*it == '.')
*it = ' ';
std::cout << result << '\n';
}
}
Live example: https://ideone.com/8zP17O
So:
in std::fixedfield mode, std::setprecision sets the maximum number of significant figures, not decimal places;
if you flip to std::fixed it means exact number of decimal places;
C++ does not provide any analogue to the %.Nf printf format string! (up to N decimal places)
I've just thrown together a little fake I/O manipulator that can get the behaviour we both want. It's not terribly performant, but it does the job:
#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>
#include <cstdio>
struct sprintf_wrapper
{
sprintf_wrapper(std::string fmt_str)
: _fmt_str(std::move(fmt_str))
{}
struct proxy
{
proxy(const sprintf_wrapper& wrapper, std::ostream& os)
: _wrapper(wrapper)
, _os(os)
{}
std::ostream& Resolve(const double value) const
{
// First find out how many characters we're going to need
const int len = std::snprintf(nullptr, 0, _wrapper._fmt_str.c_str(), value);
if (len < 0)
{
_os.setstate(std::ios::failbit);
return _os;
}
// Then allocate a buffer
std::string result;
result.resize(len);
// Actually serialise the value according to the format string
if (std::sprintf(result.data(), _wrapper._fmt_str.c_str(), value) < 0)
{
_os.setstate(std::ios::failbit);
return _os;
}
// Stream it out
_os.write(result.data(), result.size());
return _os;
}
friend std::ostream& operator<<(const proxy& obj, const double val)
{
return obj.Resolve(val);
}
private:
const sprintf_wrapper& _wrapper;
std::ostream& _os;
};
friend proxy operator<<(std::ostream& os, const sprintf_wrapper& obj)
{
return proxy(obj, os);
}
private:
std::string _fmt_str;
};
inline auto sprintf_f(size_t n, const bool showpos = false)
{
std::stringstream fmt;
fmt << '%';
if (showpos) fmt << '+';
fmt << '.' << n << 'f';
return sprintf_wrapper(fmt.str());
}
int main()
{
std::cout << sprintf_f(2) << 4.123456789 << '\n';
std::cout << sprintf_f(3) << 4.123456789 << '\n';
std::cout << sprintf_f(4) << 4.123456789 << '\n';
std::cout << sprintf_f(5) << 4.123456789 << '\n';
std::cout << sprintf_f(6) << 4.123456789 << '\n';
}
(live demo)
Use a combination of std::fixed and std::setprecision.
#include <iomanip>
#include <iostream>
int main() {
double d = 311.3456;
std::cout << std::fixed;
std::cout << std::setprecision(4);
std::cout << d << std::endl;
}
I have a string: (66)
Then I convert it to double and do some math: atof(t.c_str()) / 30
then I convert it back to string: string s = boost::lexical_cast<string>(hizdegerd)
Problem is when I show it on label it becomes 2,20000001.
I've tried everything. sprintf etc.
I want to show only one digit after point.
hizdegerd = atof(t.c_str()) / 30;
char buffer [50];
hizdegerd=sprintf (buffer, "%2.2f",hizdegerd);
if(oncekideger != hizdegerd)
{
txtOyunHiz->SetValue(hizdegerd);
oncekideger = hizdegerd;
}
I think I'd wrap the formatting up into a function template, something like this:
#include <iostream>
#include <sstream>
#include <iomanip>
template <class T>
std::string fmt(T in, int width = 0, int prec = 0) {
std::ostringstream s;
s << std::setw(width) << std::setprecision(prec) << in;
return s.str();
}
int main(){
std::string s = fmt(66.0 / 30.0, 2, 2);
std::cout << s << "\n";
}
You can use this way of conversion back to string and then only the wished number of digits for the precision will be taken in consideration:
ostringstream a;
a.precision(x); // the number of precision digits will be x-1
double b = 1.45612356;
a << b;
std::string s = a.str();
Since you wrote "I want to show":
#include<iostream>
#include<iomanip>
int main()
{
std::cout << std::fixed << std::setprecision(1) << 34.2356457;
}
Output:
34.2
By the way, sprintf is buffer-overflow-vulnerable and is not C++ .
I have an 8 digit integer which I would like to print formatted like this:
XXX-XX-XXX
I would like to use a function that takes an int and returns a string.
What's a good way to do this?
This is how I'd do it, personally. Might not be the fastest way of solving the problem, and definitely not as reusable as egrunin's function, but it strikes me as both clean and easy to understand. I'll throw it in the ring as an alternative to the mathier and loopier solutions.
#include <sstream>
#include <string>
#include <iomanip>
std::string format(long num) {
std::ostringstream oss;
oss << std::setfill('0') << std::setw(8) << num;
return oss.str().insert(3, "-").insert(6, "-");
};
Tested this, it works.
The format parameter here is "XXX-XX-XXX", but it only looks at (and skips over) the dashes.
std::string foo(char *format, long num)
{
std::string s(format);
if (num < 0) { return "Input must be positive"; }
for (int nPos = s.length() - 1; nPos >= 0; --nPos)
{
if (s.at(nPos) == '-') continue;
s.at(nPos) = '0' + (num % 10);
num = num / 10;
}
if (num > 0) { return "Input too large for format string"; }
return s;
}
Usage:
int main()
{
printf(foo("###-##-###", 12345678).c_str());
return 0;
}
Here's a bit different way that tries to work with the standard library and get it to do most of the real work:
#include <locale>
template <class T>
struct formatter : std::numpunct<T> {
protected:
T do_thousands_sep() const { return T('-'); }
std::basic_string<T> do_grouping() const {
return std::basic_string<T>("\3\2\3");
}
};
#ifdef TEST
#include <iostream>
int main() {
std::locale fmt(std::locale::classic(), new formatter<char>);
std::cout.imbue(fmt);
std::cout << 12345678 << std::endl;
return 0;
}
#endif
To return a string, just write to a stringstream, and return its .str().
This may be overkill if you only want to print out one number that way, but if you want to do this sort of thing in more than one place (or, especially, if you want to format all numbers going to a particular stream that way) it becomes more reasonable.
Here's a complete program that shows how I'd do it:
#include <iostream>
#include <iomanip>
#include <sstream>
std::string formatInt (unsigned int i) {
std::stringstream s;
s << std::setfill('0') << std::setw(3) << ((i % 100000000) / 100000) << '-'
<< std::setfill('0') << std::setw(2) << ((i % 100000) / 1000) << '-'
<< std::setfill('0') << std::setw(3) << (i % 1000);
return s.str();
}
int main (int argc, char *argv[]) {
if (argc > 1)
std::cout << formatInt (atoi (argv[1])) << std::endl;
else
std::cout << "Provide an argument, ya goose!" << std::endl;
return 0;
}
Running this with certain inputs gives:
Input Output
-------- ----------
12345678 123-45-678
0 000-00-000
7012 000-07-012
10101010 101-01-010
123456789 234-56-789
-7 949-67-289
Those last two show the importance of testing. If you want different behaviour, you'll need to modify the code. I generally opt for silent enforcement of rules if the caller can't be bothered (or is too stupid) to follow them but apparently some people like to use the principle of least astonishment and raise an exception :-)
You can use the std::ostringstream class to convert the number to a string. Then you can use the string of digits and print them using whatever formatting you want, as in the following code:
std::ostringstream oss;
oss << std::setfill('0') << std::setw(8) << number;
std::string str = oss.str();
if ( str.length() != 8 ){
// some form of handling
}else{
// print digits formatted as desired
}
int your_number = 12345678;
std::cout << (your_number/10000000) % 10 << (your_number/1000000) % 10 << (your_number/100000) %10 << "-" << (your_number/10000) %10 << (your_number/1000) %10 << "-" << (your_number/100) %10 << (your_number/10) %10 << (your_number) %10;
http://www.ideone.com/17eRv
Its not a function, but its a general method for parsing an int number by number.
#include <iostream>
#include <string>
using namespace std;
template<class Int, class Bi>
void format(Int n, Bi first, Bi last)
{
if( first == last ) return;
while( n != 0 ) {
Int t(n % 10);
n /= 10;
while( *--last != 'X' && last != first);
*last = t + '0';
}
}
int main(int argc, char* argv[])
{
int i = 23462345;
string s("XXX-XX-XXX");
format(i, s.begin(), s.end());
cout << s << endl;
return 0;
}
How's this?
std::string format(int x)
{
std::stringstream ss
ss.fill('0');
ss.width(3);
ss << (x / 10000);
ss.width(1);
ss << "-";
ss.width(2);
ss << (x / 1000) % 100;
ss.width(1);
ss << "-";
ss.width(3);
ss << x % 1000;
return ss.str();
}
Edit 1: I see strstream is deprecated and replaced with stringstream.
Edit 2: Fixed issue of missing leading 0's. I know, it's ugly.
Obviously a char * and not a string, but you get the idea. You'll need to free the output once you're done, and you should probably add error checking, but this should do it:
char * formatter(int i)
{
char *buf = malloc(11*sizeof(char));
sprintf(buf, "%03d-%02d-%03d", i/100000, (i/1000)%100, i%1000);
return buf;
}
You don't require malloc or new, just define buf as char buff[11];