Extract a floating point number from a CString - c++

I want to extract a floating point number from a CString formatted as: (example extract 22.760348)
Incidence_angle(inc)[deg] :22.760348
Basically I am reading a plain text file containing some parameters, and I want to perform some calculations on the values. I read the file using a CStdioFile object and extracting each line using the readString method as follows:
CStdioFile result(global::resultFile,CFile::modeRead);
while( result.ReadString(tmp) )
{
if(tmp.Find(L"Incidence_angle(inc)[deg]") != -1)
{
//extract value of theeta i here
// this is probably wrong
theeta_i = _tscanf(L"Incidence_angle(inc)[deg] :%f",&theeta_i);
}
}
I tried using scanf because I couldnt think of any other way.
I apologize if this question seems very basic and stupid, but I have been stuck on it for a long time and would apppriciate some help.
edit: took out the proof of concept program I wrote, caused confusion

Assuming that tmp is CString, the correct code is
CStdioFile result(global::resultFile,CFile::modeRead);
while( result.ReadString(tmp) )
{
if (swscanf_s(tmp, L"Incidence_angle(inc)[deg] :%f", &theeta_i) == 1)
{
// Use the float falue
}
}

Why not use atof?
Example taken from the link:
/* atof example: sine calculator */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main ()
{
double n,m;
double pi=3.1415926535;
char szInput [256];
printf ( "Enter degrees: " );
gets ( szInput );
n = atof ( szInput );
m = sin (n*pi/180);
printf ( "The sine of %f degrees is %f\n" , n, m );
return 0;
}

Why not do it the C++ way altogether?
This is just a hint:
#include <iostream>
#include <string>
#include <sstream>
int main()
{
double double_val=0.0;
std::string dump("");
std::string oneline("str 123.45 67.89 34.567"); //here I created a string containing floating point numbers
std::istringstream iss(oneline);
iss>>dump;//Discard the string stuff before the floating point numbers
while ( iss >> double_val )
{
std::cout << "floating point number is = " << double_val << std::endl;
}
return 0;
}
If you want to use as you have illustrated, using cstring only, try strtod() also.
Source: man -s 3 strtod

_tscanf() returns the number of assignments made, not the value read:
theeta_i = _tscanf(L"Incidence_angle(inc)[deg] :%f",&theeta_i);
so theeta_i will contain 1(.0) if a float was successfully read. Change to:
if (1 == _tscanf(L"Incidence_angle(inc)[deg] :%f",&theeta_i))
{
/* One float value successfully read. */
}
That should be _stscanf() to read from a buffer, _tscanf() will be waiting for input from standard input.

Related

Rounding converting from string to double/float

I am trying to extract a number from a string and convert it into a double or float so I can do some numerical operations on it. I am able to isolate the variable I need so the string consists only of the number, but when I try to convert it to a float or double it rounds the value, ie from 160430.6 to 160431.
//Helper Function to Extract Value of Interest
//Based on column of final digit of numbers being same across various FLOPS output files
double findValue(string &line, int &refN){
setprecision(100);
string output;
//go to end column and work backwards to get value string
while(line[refN] != ' '){
output = line[refN] + output;
refN = refN - 1;
}
const char* outputx = output.c_str();
double out = atof(outputx);
//removing the const char* line and replacing atof with stod(output) runs into the same issue
return out;
}
int main()
{
string name;
cin >> name;
ifstream file(name);
//opens file
if(!file.is_open()){"error while opening the file";
}else{
//Temporary Reference Definitions
string ref = "TOGW";
int refN = 25;
string line = findLine(file,ref);
double MTOGW = findValue(line, refN);
cout << MTOGW;
}
return 0;
}
I initially tried using stof() to convert, but that rounded. I have also tried using stod() and stold(), and last tried converting to a const char* and using atof(). I have messed with the setprecision() value, but also have not been able to solve it that way.
I cannot use Boost
You were almost there. The rounding was occurring on output, so that's where you need to use setprecision. That and always use double instead of float to ensure you have enough precision in your variables.
#include <vector>
#include <ranges>
#include <iomanip>
#include <iostream>
#include <string>
using std::string;
double findValue(string &line, int &refN){
//setprecision(100);
string output;
//go to end column and work backwards to get value string
while(line[refN] != ' '){
output = line[refN] + output;
refN = refN - 1;
}
const char* outputx = output.c_str();
double out = strtod(outputx, NULL);
return out;
}
int main()
{
string s = " 160430.6";
int n = s.size() - 1;
std::cout << std::setprecision(10) << findValue(s, n) << '\n';
}
See it in action on the Godbolt compiler.

How to convert a string into a long double?

To start off, I have thoroughly examined other questions on that matter and nothing helped me. I need to convert a string into a long double type, and nothing works for me.
string a="634.232";
long double x;
x=strtold(a.c_str(),0);
For example: this "strtold" changes this input into this output.
Check for errors:
#include <cerrno>
#include <cstdlib>
#include <iostream>
const char str[] = "634.232";
int main()
{
char * e;
errno = 0;
long double val = std::strtold(str, &e);
if (*e != '\0' || errno != 0) { /* error */ std::abort(); }
std::cout << "SUccessfully parsed: " << val;
}
Note that string parsing can fail in multiple ways: The string may not, or not in its entirety, represent a number, or the number that it does represent may be too large to fit into the data type. You have to check for all those possibilities, which is what this code is doing. The end pointer e checks that we've consumed the entire string, and the errno checks that the conversion succeeded.
strtold should work fine, as KerrekSB suggests.
But while you're checking your work I'd recommend using stold: http://www.cplusplus.com/reference/string/stold/
string a{"634.232"};
long double x{stold(a)};

sprintf too many/few decimals

I must convert decimal numbers using a non-scientific (ie. no mantissa/exponent/E's) character string. My code looks like this:
/*!
\brief Converts a <em>XML Schema Decimal</em>
*/
char *ToDecimal(double val) const
{
const size_t nMax = 200;
char *doubleStr = new char[nMax];
sprintf(doubleStr, "%1.6f", val);
return doubleStr;
}
The problem is that when the input val is 1 then the function returns 1.000000 but I was hoping for a output of 1. Also, if I change the code to sprintf(doubleStr, "%1.0f", val); then it correctly outputs 1, but if the input val is changed to 0.000005 the output is 0, and I was hoping the output would then be 0.000005. So basically I want all output to be as short as possible and remove all unnessesary 0's. Is this possible with sprintf? I would like to support 3.4E +/- 38 range with this function.
It turns out that c++ iostreams (specifically, ostringstream) are better suited for your task than sprintf.
Use the std::fixed manipulator to disable scientific notation. Use std::setprecision to specify precision (number of characters after decimal dot). In your case, precision of 45 places seems enough to represent all float numbers.
#include <sstream>
#include <string>
#include <iostream>
#include <iomanip>
std::string convert(double x)
{
std::ostringstream buffer;
buffer << std::fixed << std::setprecision(45) << x;
std::string result = buffer.str();
return result;
}
In addition, to clean-up the result, remove any trailing zeros.
size_t i = result.find_last_not_of('0');
if (result[i] != '.')
++i;
result.erase(i);
Note: the clean-up of trailing zeros will only work for numbers that are exactly representable (like 0.75 or 0.03125): for example, the number 0.1 is converted to 0.10000000000000000555111512312578270211815834. One could use non-constant precision (depending on the magnitude of the number), but this is very tricky to get right.
Instead, it's possible to use the following ugly (and slow) hack: try converting the start of the string back to double, and cut the string if the result is equal to the initial number.
size_t i;
for (i = 1; i < result.size(); ++i)
{
std::istringstream cut(result.substr(0, i));
double temp;
cut >> temp; // the verbose syntax could fit into one line
if (temp == x) // by using boost::lexical_cast
break;
}
Since this is tagged C++, I assume you can use C++ features that are not available in C (if not, tell me and I'll delete this answer).
First, I suggest using a std::string instead of char* (this frees you from memory managing the buffer)? Second, I suggest using a ostringstream for the conversion:
#include <sstream>
std::string ToDecimal(double val) {
std::ostringstream oss;
oss << val;
return oss.str();
}

Parsing command line char arguments as ints with error checking in C++

I'm trying to write a program that takes in two ints as command line arguments. The ints both need to be greater than 0. I understand that I need to convert from char but I have only ever done that using atoi which I now know that I shouldn't do. I've seen people use sstreams and strtol but I'm not sure how those would work in this case. What is the best way to accomplish this?
#include <iostream>
#include <string>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
const int N = 7;
const int M = 8;//N is number of lines, M number of values
//--------------
//-----Main-----
//--------------
int main(int argc, char* argv[])
{
if((argc != 0) && (argv[0] != NULL) && (argv[1] != NULL))
{
N = argv[0];
M = argv[1];
}
else
{
cout << "Invalid or no command line arguments found. Defaulting to N=7 M=8.\n\n" << endl;
}
//Blah blah blah code here
return 0;
}
In C++11 there's stoi, stol, stoll for this: http://en.cppreference.com/w/cpp/string/basic_string/stol
Those throw invalid_argument or out_of_range exceptions if the string isn't in the right format.
There's nothing particularly wrong about using atoi, except it doesn't have a mechanism to report exceptions because it's a C function. So you only have the return value - the problem is all return values of atoi are valid values, so there's no way to differentiate between the return value of 0 as the correct parsing of "0" or the failure of parsing. Also, atoi doesn't do any checks for whether the value is outside the available value range. The first problem is easy to fix by doing the check yourself, the second is more difficult because it involves actually parsing the string - which kind of defeats the point of using an external function in the first place.
You can use istringstream like this:
Pre-C++11:
int val;
std::istringstream iss(arg[i]);
iss >> val;
if (iss.fail()) {
//something went wrong
} else {
//apparently it worked
}
C++11:
int val;
std::istringstream iss(arg[i]);
iss >> val;
if(iss.fail()) {
if(!value) {
//wrong number format
} else if(value == std::numeric_limits<int>::max() ||
value == std::numeric_limits<int>::min()
{
//number too large or too small
}
} else {
//apparently it worked
}
The difference is that pre C++11, only format errors were detected (according to standard), also it wouldn't overwrite the value on error. In C++11, values are overwritten by either 0 if it's a format error or max/min if the number is too large or too small to fit into the type. Both set the fail flag on the stream to indicate errors.
In this specific case, atoi will work fine. The problem with atoi is that you can't differentiate between its returning 0 to signify an error of some sort, and its returning 0 to indicate that the input was 0.
In your case, however, a valid input must be greater than 0. You don't care whether the input was 0 or something else that couldn't be converted. Either way you're doing to set it to the default value.
As such, I'd do something like:
int convert(char *input, int default) {
int x = atoi(input);
return x==0 ? default : x;
}
if (argc > 1)
N = convert(argv[1], 7);
if (argc > 2)
M = convert(argv[2], 8);
Note that argv[0] traditionally holds the name of the program being invoked. Arguments passed on the command line are received as argv[1] through argv[argc-1].
First, you can't use const qualifier for M and N, since you will change their value:
int N = 7;
int M = 8;//N is number of lines, M number of values
Second, you don't need to check for (argv[0] != NULL) && (argv[1] != NULL), just check if argc (argument count) is greater or equal to 3:
if(argc >= 3)
Then you need to convert this into integers. If you don't want to use atoi, and if you don't have C++11 compiler you should use C++'s stringstream or C's strtol
stringstream ss;
int temp;
ss << argv[1]; // Put string into stringstream
ss >> temp; // Get integer from stringstream
// Check for the error:
if(!ss.fail())
{
M = temp;
}
// Repeat
ss.clear(); // Clear the current content!
ss << argv[2]; // Put string into stringstream
ss >> temp; // Get integer from stringstream
// Check for the error:
if(!ss.fail())
{
N = temp;
}
so, whole code will look like this:
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <sstream>
using namespace std;
int N = 7;
int M = 8;//N is number of lines, M number of values
//--------------
//-----Main-----
//--------------
int main(int argc, char* argv[])
{
if(argc >= 3)
{
stringstream ss;
int temp;
ss << argv[1]; // Put char into stringstream
ss >> temp; // Get integer from stringstream
// Check for the error:
if(!ss.fail())
{
M = temp;
}
// Repeat
// empty
ss.clear();
ss << argv[2]; // Put char into stringstream
ss >> temp; // Get integer from stringstream
// Check for the error:
if(!ss.fail())
{
N = temp;
}
cout << M << " " << N;
}
else
{
cout << "Invalid or no command line arguments found. Defaulting to N=7 M=8.\n\n" <<
endl;
}
//Blah blah blah code here
return 0;
}
Also, C header files include with c prefix, not with .h suffix (<cstdio> instead of <stdio.h>)

Handling of conversions from and to hex

I want to build a function to easily convert a string containing hex code (eg. "0ae34e") into a string containing the equivalent ascii values and vice versa.
Do I have to cut the Hex string in pairs of 2 values and gue them together again or is there a convenient way to do that?
thanks
Based on binascii_unhexlify() function from Python:
#include <cctype> // is*
int to_int(int c) {
if (not isxdigit(c)) return -1; // error: non-hexadecimal digit found
if (isdigit(c)) return c - '0';
if (isupper(c)) c = tolower(c);
return c - 'a' + 10;
}
template<class InputIterator, class OutputIterator> int
unhexlify(InputIterator first, InputIterator last, OutputIterator ascii) {
while (first != last) {
int top = to_int(*first++);
int bot = to_int(*first++);
if (top == -1 or bot == -1)
return -1; // error
*ascii++ = (top << 4) + bot;
}
return 0;
}
Example
#include <iostream>
int main() {
char hex[] = "7B5a7D";
size_t len = sizeof(hex) - 1; // strlen
char ascii[len/2+1];
ascii[len/2] = '\0';
if (unhexlify(hex, hex+len, ascii) < 0) return 1; // error
std::cout << hex << " -> " << ascii << std::endl;
}
Output
7B5a7D -> {Z}
An interesting quote from the comments in the source code:
While I was reading dozens of programs that encode or decode the
formats here (documentation? hihi:-) I have formulated Jansen's
Observation:
Programs that encode binary data in ASCII are written in such a style
that they are as unreadable as possible. Devices used include
unnecessary global variables, burying important tables in unrelated
sourcefiles, putting functions in include files, using
seemingly-descriptive variable names for different purposes, calls to
empty subroutines and a host of others.
I have attempted to break with this tradition, but I guess that that
does make the performance sub-optimal. Oh well, too bad...
Jack Jansen, CWI, July 1995.
If you want to use a more c++ native way, you can say
std::string str = "0x00f34" // for example
stringstream ss(str);
ss << hex;
int n;
ss >> n;
The sprintf and sscanf functions can already do that for you. This code is an example that should give you an idea. Please go through the function references and the safe alternatives before you use them
#include <stdio.h>
int main()
{
int i;
char str[80]={0};
char input[80]="0x01F1";
int output;
/* convert a hex input to integer in string */
printf ("Hex number: ");
scanf ("%x",&i);
sprintf (str,"%d",i,i);
printf("%s\n",str);
/* convert input in hex to integer in string */
sscanf(input,"%x",&output);
printf("%d\n",output);
}