std::stof precision problems - c++

I've got a .txt file with multiple x y and z float numbers and I'm getting line by line with std::getline(file, line).
My problem is: while I'm getting the values correctly for x, y and z in strings, their decimal places are being reduced and that's not what I want.
I want to know how to store the full value. From what I saw I can use std::setprecision() to correct this while printing, but I want to correct the stored values so I can use them.
What can I do? Are the numbers stored properly but not shown properly by my std::cout? Here's the code:
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <regex>
int main()
{
std::string line;
std::ifstream input("input.txt");
std::vector<float> x;
std::vector<float> y;
std::vector<float> z;
std::regex reg("[,]+");
int line_count = 0;
if (input.is_open()) {
while (std::getline(input, line)) {
if (line_count > 0)
{
std::sregex_token_iterator iter(line.begin(), line.end(), reg, -1);
std::sregex_token_iterator end;
std::vector<std::string> tokens(iter, end);
std::cout << tokens[0] << std::endl;
x.push_back(std::stof(tokens[0]));
std::cout << x[line_count - 1] << std::endl;
y.push_back(std::stof(tokens[1]));
z.push_back(std::stof(tokens[2]));
}
line_count++;
}
}
/*for (size_t i = 0; i < x.size(); i++)
{
std::cout << x[i] << " " << y[i] << " " << z[i] << std::endl;
}*/
}
The .txt file is as follows:
x,y,z
-0.015869140625,0.896728515625,-0.103515625
-0.00634765625,0.8935546875,-0.147216796875
-0.00634765625,0.8935546875,-0.147216796875
-0.02197265625,0.9326171875,-0.10400390625
-0.02197265625,0.9326171875,-0.10400390625
-0.078369140625,0.944580078125,-0.126220703125
-0.078369140625,0.944580078125,-0.126220703125
-0.047119140625,0.979248046875,-0.114990234375
-0.047119140625,0.979248046875,-0.114990234375
0.022216796875,1.0068359375,-0.096435546875
-0.009033203125,1.02685546875,-0.078369140625
-0.009033203125,1.02685546875,-0.078369140625
-0.052490234375,1.033935546875,-0.114501953125

Double and float do not have that precision.
You can use The GNU Multiple Precision Arithmetic Library class mpf_class.

Related

std::stof() rounds numbers, how to avoid

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.

Read and print a csv file with more than 2 column in c++ using multimap

I'm a beginner in c++ and required to write a c++ program to read and print a csv file like this.
DateTime,value1,value2
12/07/16 13:00,3.60,50000
14/07/16 20:00,4.55,3000
May I know how can I proceed with the programming?
I manage to get the date only via a simple multimap code.
I spent some time to make almost (read notice at the end) exact solution for you.
I assume that your program is a console application that receives the original csv-file name as a command line argument.
So see the following code and make required changes if you like:
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <map>
#include <string>
std::vector<std::string> getLineFromCSV(std::istream& str, std::map<int, int>& widthMap)
{
std::vector<std::string> result;
std::string line;
std::getline(str, line);
std::stringstream lineStream(line);
std::string cell;
int cellCnt = 0;
while (std::getline(lineStream, cell, ','))
{
result.push_back(cell);
int width = cell.length();
if (width > widthMap[cellCnt])
widthMap[cellCnt] = width;
cellCnt++;
}
return result;
}
int main(int argc, char * argv[])
{
std::vector<std::vector<std::string>> result; // table with data
std::map<int, int> columnWidths; // map to store maximum length (value) of a string in the column (key)
std::ifstream inpfile;
// check file name in the argv[1]
if (argc > 1)
{
inpfile.open(argv[1]);
if (!inpfile.is_open())
{
std::cout << "File " << argv[1] << " cannot be read!" << std::endl;
return 1;
}
}
else
{
std::cout << "Run progran as: " << argv[0] << " input_file.csv" << std::endl;
return 2;
}
// read from file stream line by line
while (inpfile.good())
{
result.push_back(getLineFromCSV(inpfile, columnWidths));
}
// close the file
inpfile.close();
// output the results
std::cout << "Content of the file:" << std::endl;
for (std::vector<std::vector<std::string>>::iterator i = result.begin(); i != result.end(); i++)
{
int rawLen = i->size();
for (int j = 0; j < rawLen; j++)
{
std::cout.width(columnWidths[j]);
std::cout << (*i)[j] << " | ";
}
std::cout << std::endl;
}
return 0;
}
NOTE: Your task is just to replace a vector of vectors (type std::vector<std::vector<std::string>> that are used for result) to a multimap (I hope you understand what should be a key in your solution)
Of course, there are lots of possible solutions for that task (if you open this question and look through the answers you will understand this).
First of all, I propose to consider the following example and to try make your task in the simplest way:
#include <iostream>
#include <sstream>
#include <vector>
#include <string>
using namespace std;
int main()
{
string str = "12/07/16 13:00,3.60,50000";
stringstream ss(str);
vector<string> singleRow;
char ch;
string s = "";
while (ss >> ch)
{
s += ch;
if (ss.peek() == ',' || ss.peek() == EOF )
{
ss.ignore();
singleRow.push_back(s);
s.clear();
}
}
for (vector<string>::iterator i = singleRow.begin(); i != singleRow.end(); i++)
cout << *i << endl;
return 0;
}
I think it can be useful for you.

Sorting a 2D vector of doubles

I have a list of data (in 4 columns) that I would like to sort by a certain column. It was read in from a file to a 2D vector. I the used the std::sort method and wrote my comparator functor. The program compiles and runs, but when I print the first 10 elements it is not sorted, but is certainly different from the order it was added to the 2D vector.
Here is the code:
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <string>
#include <vector>
#include <algorithm>
#include <iomanip>
using namespace std;
typedef vector<double> Row;
typedef vector<Row> Matrix;
bool sortByFourthColumn(const Row& row1, const Row& row2){
return (double) row1[3] < (double) row2[3];
}
int main(){
std::ifstream infile;
infile.open("Test8_Output.txt");
double x,y,z,E;
char line[200];
int line_count=0;
ofstream outfile;
outfile.open("WO_crp.txt");
if (infile.is_open()){
while (!infile.eof()){
infile.getline(line,170);
if (line[0] != '%'){
outfile<<line<<"\n";
line_count++;
}
else{
}
}
Matrix data(line_count,Row(4));
outfile.close();
std::ifstream myfile;
myfile.open("WO_crp.txt");
int i = 0;
while(myfile >> x >> y >> z >> E){
data[0][i] = x;
data[1][i] = y;
data[2][i] = z;
data[3][i] = E;
i++;
}
myfile.close();
std::sort(data.begin(), data.end(), sortByFourthColumn);
for (int u = 0; u <20; u++){
cout << setprecision(5) << data[0][u] << "\t" << setprecision(5)<< data[1][u] << "\t" << setprecision(5)<< data[2][u] << "\t" << setprecision(5)<< data[3][u] << endl;
}
}
else{
cout << "Error: File is invalid.\n";
}
return(0);
}
EDIT - Sample of what the input file looks like:
EDIT 2 - swapped 4 and line_count in Matrix data(4,Row(line_count));
% Model: CDS_Test8.mph
% Version: COMSOL 5.2.0.220
% Date: Jul 13 2016, 14:33
% Dimension: 3
% Nodes: 86183
% Expressions: 1
% Description: Electric field norm
% Length unit: m
% x y z es.normE (V/m)
0.13774675805195374 0.05012986567931247 0.20735 67.35120820901535
0.13870000000000005 0.04957489750396299 0.20735000000000003 102.8772500513651
0.13870000000000002 0.050800000000000005 0.20735 87.56008679032011
0.13792733849817027 0.050131465727838186 0.20801419247484804 73.55192534768238
0.13674627634411463 0.04992349737428063 0.20735 63.23018910026428
0.13750191177019236 0.0508 0.20735000000000003 67.26176884022838
0.13827743496772454 0.05193409099097887 0.20734999999999998 73.35474409597487
0.13803618792088135 0.05134931748395268 0.20841988134890965 75.3712126982815
0.13905949760011943 0.05141879754884912 0.20734999999999998 83.70739713476813
0.13896970815034013 0.05092428105421264 0.208142746399683 84.73571510992372
0.1390220807917094 0.0501245422629353 0.20817502757007986 85.57119242707628
0.13944867847480893 0.05161480113017738 0.2081969878426443 89.65643851109644
And so on it goes for another 87k lines or so.
I have a list of data (in 4 columns) that I would like to sort by a
certain column.
The problem is that the dimensions of the vector of vectors used to store the data in OP program is not consistent between declaration and use.
A minor problem is the use of while(!infile.eof()){... which should be avoided.
A fixed version is like this:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <array>
#include <algorithm>
#include <iomanip>
using Row = std::array<double,4>; // instead of typedefs
using Matrix = std::vector<Row>;
using std::cout;
bool sortByFourthColumn(const Row& row1, const Row& row2){
return row1[3] < row2[3];
// ^ The cast is unnecessary
}
int main(){
std::string file_name{"Test8_Output.txt"};
std::ifstream infile{file_name, std::ios_base::in};
if ( !infile ) {
cout << "Error: unable to open file " << file_name << '\n';
return EXIT_FAILURE;
}
Matrix data;
data.reserve(90000); // if you are afraid of the reallocations
int count = 0;
std::string line;
// instead of two loops you can use one loop and read the file once
// I'll use std::getline to extract a row in a std::string
while ( std::getline(infile, line) ) {
// skip comments and empty lines
if ( line.empty() || line[0] == '%' )
continue;
++count;
// extract data from the string using a stringstream
std::stringstream ss{line};
Row r;
ss >> r[0] >> r[1] >> r[2] >> r[3];
if ( !ss ) {
cout << "Format error in line " << count << " of file.\n";
break;
}
data.push_back(std::move(r));
}
std::sort(data.begin(), data.end(), sortByFourthColumn);
cout << std::setprecision(5) << std::fixed;
for ( const auto & r : data ) {
for ( auto const &x : r ) {
cout << std::setw(10) << x;
}
cout << '\n';
}
return EXIT_SUCCESS;
}
The output, given the example data is:
0.13675 0.04992 0.20735 63.23019
0.13750 0.05080 0.20735 67.26177
0.13775 0.05013 0.20735 67.35121
0.13828 0.05193 0.20735 73.35474
0.13793 0.05013 0.20801 73.55193
0.13804 0.05135 0.20842 75.37121
0.13906 0.05142 0.20735 83.70740
0.13897 0.05092 0.20814 84.73572
0.13902 0.05012 0.20818 85.57119
0.13870 0.05080 0.20735 87.56009
0.13945 0.05161 0.20820 89.65644
0.13870 0.04957 0.20735 102.87725

Declaring an array of type String containing numbers with padding

I am trying to create an array of String that contain numbers. These numbers are the names of folders that I need to access. Currently I am declaring it as shown below:
String str1[] = { "001", "002", "003", "004", "005", "006", "007", "008", "009", "010", "011", "012", "013", "014", "015", "016", "017", "018", "019", "020", };
I have 124 folders and naming them in such fashion is tedious. Is there a better way to do this? I am working with C++.
You can use stringstreams and set the format options to fill the integer to a certain number of characters and set the filling character.
Edit: Ok my code doesn't begin with 1 but 0, but I'm sure you can figure that out :)
#include <iostream>
#include <string>
#include <sstream>
#include <iomanip>
#include <vector>
using namespace std;
int main()
{
std::vector<std::string> strs;
for (int i = 0; i < 124; i++)
{
std::ostringstream os;
os << std::setfill('0') << std::setw(3) << i;
strs.push_back(os.str());
}
for (const auto& s : strs)
{
std::cout << s << "\n";
}
}
Live example: http://ideone.com/TEV2iq
use a stringstream and for loop.
Example:
uint32_t t150()
{
std::vector<std::string> strVec; // i.e. String str1[]
for (int i=1; i<125; ++i)
{
std::stringstream ss;
ss << std::setw(3) << std::setfill('0') << i;
strVec.push_back(ss.str());
}
for (int i=0; i<124; ++i)
std::cout << strVec[i] << std::endl;
return(0);
}
An alternative is something like:
std::string t150b(int i) {
std::stringstream ss;
ss << std::setw(3) << std::setfill('0') << i;
return (ss.str());
}
// not tested, and no range check
which returns the formatted string for the value i ... I imagine you have the loop at some higher level code.
Another alternative is to skip the vector, just build the string with white spaces between ... then fetch them like you fetch any file item ...
void t150c(std::stringstream& ss)
{
for (int i=1; i<125; ++i) {
ss << std::setw(3) << std::setfill('0') << i << " ";
// white space between values -------------------^^
}
}
Usage example:
{
std::stringstream ss;
t150c(ss); // 'fill' stream with desired strings
do {
if(ss.eof()) break;
std::string s;
ss >> s; // extract string one at a time
std::cout << s << std::endl; // and use
}while(1);
}
std::string str1[124];
for(int i = 1; i <= 124; i++){
str1[i-1] = convertTo3Digit(i);
}
Then just write the convertTo3Digit function to take the numerical value and format it into a 3-digit string.
Another less elegant way would be to format a column in excel to be three-digit numbers and generate 001-124 and then copy-paste into your static initializer. You can use regex to add the quotes and commas.

error with std::ostringsteam and std::string

Hi i want to save many different csv files from a function with a naming convention based on a different double value. I do this with a for loop and pass a string value to save each .csv file differently. Below is an example of what I'm trying to do the desired result would be
1.1_file.csv
1.2_file.csv
but instead i get
1.1_file.csv
1.11.2_file.csv
Here is a working sample code, what can i do to fix this
#include <sstream>
#include <iomanip>
#include <cmath>
#include <iostream>
#include <vector>
int main(){
std::string file = "_file.csv";
std::string s;
std::ostringstream os;
double x;
for(int i = 0; i < 10; i++){
x = 0.1 + 0.1 *i;
os << std::fixed << std::setprecision(1);
os << x;
s = os.str();
std::cout<<s+file<<std::endl;
s.clear();
}
return 0;
}
The ostringstream doesn't reset on each iteration of the loop, so you are just adding x to it every iteration; put it inside the scope of the for to make os be a different clean object on each iteration, or reset the contents with os.str("").
Also, the variable s is unnecessary; you can just do
std::cout << os.str() + file << std::endl;
And you don't need s and you eliminate the overhead of making a copy of the string.
Your ostringstream is getting appended for each iteration of the loop. You should clear it and reuse it as shown below (courtesy: How to reuse an ostringstream? on how to reuse an ostringstream)
#include <sstream>
#include <iomanip>
#include <cmath>
#include <iostream>
#include <vector>
int main() {
std::string file = "_file.csv";
std::string s;
double x;
std::ostringstream os;
for (int i = 0; i < 10; i++) {
x = 0.1 + 0.1 * i;
os << std::fixed << std::setprecision(1);
os << x;
s = os.str();
std::cout << s + file << std::endl;
os.clear();
os.str("");
}
return 0;
}