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();
}
Related
I am trying to convert a string that is only 1s and 0s to a decimal value. The variable value is initialized to 0 and is never updated. I suspect the problem is that binaryString[i] is treated as a string and therefore the athematic function doesn't work. How can I fix this?
void binaryToDec(string binaryString, int value)
{
int binaryStringLength = binaryString.length();
for (int i = 0; i < binaryStringLength; i++)
{
value += pow(2,i)+ binaryString[i];
}
}
I tried to use basic type casting like int(binaryString[i]) but that doesn't work.
Firstly binaryString[i] is a character, not an integer. To convert a digit character to an integer you can just subtract '0'.
binaryString[i] - '0'
Secondly pow(2,i) returns a floating point number, when you want an integer. This is inefficient, and even more seriously might be subject to rounding errors. Instead you should use a shift operator, which efficiently and accurately calculates integer powers of two.
1 << i
Thirdly you have + where you need *. The two terms should be multiplied not added.
Putting all that together you get
value += (1 << i) * (binaryString[i] - '0');
But the most serious error of all is that your function does not return a value. It should look like this
int binaryToDec(string binaryString)
{
int value = 0;
...
return value;
}
Your version passes value as a parameter, that's the wrong way round, binaryString is a parameter, but value should be returned from the function. Not sure why but this is a difference a lot of newbies struggle with.
You could construct a bitset from the string and then back to a ullong.
But, limiting the solution to a maximum binary string size.
[Demo]
#include <bitset>
#include <fmt/core.h>
#include <stdexcept> // out_of_range
auto binaryToDec(const std::string& binary) {
if (binary.size() > 64) {
throw std::out_of_range{ "binary string is too big" };
}
return std::bitset<64>{binary}.to_ullong();
}
int main() {
try {
std::string binary_1{"101010"};
fmt::print("binary: {}, decimal: {}\n", binary_1, binaryToDec(binary_1));
std::string binary_2{
"1111""1111""1111""1111"
"1111""1111""1111""1111"
"1111""1111""1111""1111"
"1111""1111""1111""1111"
"1"
};
fmt::print("{}\n", binaryToDec(binary_2));
} catch (const std::exception& e) {
fmt::print("Error: {}.\n", e.what());
}
}
// Outputs:
//
// binary: 101010, decimal: 42
// Error: binary string is too big.
How can I convert vector of floats into a char*?
I have a collection of floats stored inside std::vector<float> id_list:
0,
0,
0,
0,
0,
1.77636e-15,
2.35099e-38,
-7.10543e-15,
3.06107e-38,
....
and using this code to convert it:
char *ptr = (char*)&id_list[0];
std::string dump_string(ptr, id_list.size() * sizeof(float));
but dump_string.c_str() returns empty, despite having some values stored in my vector. I'm expecting to get all values concatenated into a one, long string, ie.:
0,0,0,0,0,1.77636e-15,2.35099e-38,-7.10543e-15,3.06107e-38........
I'm expecting to get all values concatenated into a one, long string, ie.:
0,0,0,0,0,1.77636e-15,2.35099e-38,-7.10543e-15,3.06107e-38........
You are creating a std::string that simply holds a copy of the raw bytes of the float array. So, if any of the bytes happen to contain a numeric value of 0 (as is the case in your example), you will get a truncated result if you try to treat the std::string as a null-terminated C string.
If you want to actually convert the float values into a human-readable string, you need to format the values individually, such as with std::ostringstream or std::to_string(), eg:
std::ostringstream oss;
if (!id_list.empty()) {
oss << id_list[0];
for(size_t i = 1; i < id_list.size(); ++i) {
oss << "," << id_list[i];
}
}
std::string dump_string = oss.str();
std::string dump_string;
if (!id_list.empty()) {
dump_string += std::to_string(id_list[0]);
for(size_t i = 1; i < id_list.size(); ++i) {
dump_string += ',';
dump_string += std::to_string(id_list[i]);
}
}
. I'm expecting to get all values concatenated into a one, long string, ie.:
You could write a small function to do that.
Example:
#include <iostream>
#include <iterator> // std::advance
#include <string>
#include <sstream> // std::stringstream
#include <vector>
// a function taking two iterators and streaming the content to a stringstream
// then returning a `std::string` using `stringstream::str()`
template<typename Begin, typename End = Begin>
std::string join(Begin begin, End end) {
std::stringstream retval;
if(begin != end) {
retval << *begin;
for(std::advance(begin, 1); begin != end; std::advance(begin, 1)) {
retval << ',' << *begin;
}
}
return retval.str();
}
int main()
{
std::vector<float> id_list {
0,
0,
0,
0,
0,
1.77636e-15,
2.35099e-38,
-7.10543e-15,
3.06107e-38,
};
std::cout << join(id_list.begin(), id_list.end());
}
Output:
0,0,0,0,0,1.77636e-15,2.35099e-38,-7.10543e-15,3.06107e-38
As others note, your approach of using casting is not the right way to go. Also, note that all of the value in your example are probably exactly 0.0 (or -0.0)! Why? Because they're beyond the precision range of float on typical platforms.
Still, you could concatenate the string representation of the float's in an std::vector, using the ranges library, and with no need for any loops:
#include <vector>
#include <string>
#include <iostream>
#include <range/v3/all.hpp>
std::string concat_my_floats(const std::vector<float>& vec)
{
std::ostringstream oss;
auto convert = [&](float x) { oss.str(""); oss << x; return oss.str(); };
return vec
| ranges::views::transform(convert)
| ranges::views::cache1 // We need this, see footnote.
| ranges::views::join(',')
| ranges::to<std::string>();
}
int main () {
std::vector<float> id_list = {0, 1.1, -2.2, 3.3};
std::cout << concat_my_floats(id_list) << std::endl;
}
This will give you:
0,1.1,-2.2,3.3
If you're wondering what's that cache1 business - it has to do with how the transformed range is a range of prvalues, which the ranges library is not willing to join for performance reasons; apparently you need to explicitly allow a caching of the last element, expensive though it may be. See here.
How can I convert vector of floats into a char*?
Here is one way to reinterpret the array of floating point numbers as bytes:
char *ptr = reinterpret_cast<char*>(id_list.data());
Which does exactly what your cast did except it doesn't rely on a C style cast.
but dump_string.c_str() returns empty
This is because dump_string happens to contain an empty null terminated string at the beginning.
I'm expecting to get all values concatenated into a one, long string, ie.:
0,0,0,0,0,1.77636e-15,2.35099e-38,-7.10543e-15,3.06107e-38........
You expectation is misguided. Reinterpreting bytes of floating point numbers is a separate thing from serialising them to readable text. If this is the output you want, then reinterpretation is not the operation that you need.
You can use for example a string stream to convert floating point numbers to text.
A shell of the desired code:
#include <iostream>
#include <string>
std::string str_to_bin(const std::string& str)
{
//...
}
int main()
{
std::string str = "123";
std::cout << str_to_bin(str); //would print 1111011
}
Question title says it all. I've been stuck on this for a while. Is there a solution for this in the STL? Or something simple that I'm missing? If not, how would I go about doing this? Maybe a direction you could point me to? Also, speed is of great importance.
EDIT: The number can be of any size (larger than long long as well), so std::stoi and std::bitset<> are off the table.
You can do it using GMP (GNU Multi-Precision). Something like this:
#include <gmpxx.h>
std::string str_to_bin(const std::string& str)
{
mpz_class bignum;
int rc = bignum.set_str(str, 10);
if (rc != 0)
throw std::invalid_argument("bad number: " + str);
return bignum.get_str(2);
}
Or using the traditional C API:
#include <gmp.h>
std::string str_to_bin(const std::string& str)
{
mpz_t bignum;
int rc = mpz_set_str(bignum, str.c_str(), 10);
if (rc != 0)
throw std::invalid_argument("bad number: " + str);
char* rawstr = mpz_get_str(nullptr, 2, bignum);
std::string result(rawstr);
free(rawstr);
return result;
}
Okay let's break down the process you require here. (only one of an infinite number of ways to do this)
Conversion of a number represented as a string type into an integer type.
Conversion of the intermediary integer type into a binary number which is held in another string type. (judging by the return type of your function, which could just as easily return an integer by the way and save the headache of representing the binary equivalent as a string)
For step 1:
Use the standard library function stoi(). It does what you might imagine, extracts the numerical data from the string and stores it in an integer.
std::string numberstr = "123";
int numberint = std::stoi(numberstr);
std::cout << numberint << "\n";
Now you have the number as an integer.
For step 2:
This process involves the conversion of a number from base 10 (decimal) to base 2 (binary).
Divide the number by 2.
Store the remainder and the quotient of this division operation for further use.
The remainder becomes part of the binary representation, while the quotient is used as the next dividend.
This process repeats until the dividend becomes 1, at which point it too is included in the binary representation.
Reverse the string, and voila! You now have the binary representation of a number.
If you want to handle negative numbers (which I imagine you might), simply perform a check before the conversion to see if the converted integer is negative, and set a flag to true if it is.
Check this flag before reversing, and add a negative sign to end of the string before reversing.
The final function looks like this:
std::string str_to_bin(const std::string& str)
{
std::string binarystr = ""; // Output string
int remainder;
int numberint = std::stoi(str);
bool flagnegative = false;
// If negative number, beginning of binary equivalent is 1
if (numberint < 0)
{
numberint = abs(numberint);
flagnegative = true;
}
// If number is 0, don't perform conversion simply return 0
if (numberint == 0)
{
binarystr = "0";
return binarystr;
}
std::cout << numberint << "\n";
while (numberint != 1)
{
remainder = numberint % 2;
numberint /= 2;
std::ostringstream convert; // stream used for the conversion
convert << remainder; // insert the textual representation of 'remainder' in the characters in the stream
binarystr += convert.str();
}
std::ostringstream final;
final << numberint; // To insert the last (or rather first once reversed) binary number
binarystr += final.str();
if (flagnegative == true)
binarystr += "-";
std::reverse(binarystr.begin(), binarystr.end());
return binarystr;
}
Other people have posted the STL method using bitset, which might be of value to you, but I believe there's no fun in simply copy pasting a function found online.
This way, you understand exactly whats going on under the hood!
However I cannot provide a guarantee for speed, especially since this is using streams. Bit operations would definitely be more efficient.
Anywho, hope this helps! I had quite a bit of fun writing this.
I have a string in form "blah-blah..obj_xx..blah-blah" where xx are digits. E.g. the string may be "root75/obj_43.dat".
I want to read "xx" (or 43 from the sample above) as an integer. How do I do it?
I tried to find "obj_" first:
std::string::size_type const cpos = name.find("obj_");
assert(std::string::npos != cpos);
but what's next?
My GCC doesn't support regexes fully, but I think this should work:
#include <iostream>
#include <string>
#include <regex>
#include <iterator>
int main ()
{
std::string input ("blah-blah..obj_42..blah-blah");
std::regex expr ("obj_([0-9]+)");
std::sregex_iterator i = std::sregex_iterator(input.begin(), input.end(), expr);
std::smatch match = *i;
int number = std::stoi(match.str());
std::cout << number << '\n';
}
With something this simple you can do
auto b = name.find_first_of("0123456789", cpos);
auto e = name.find_first_not_of("0123456789", b);
if (b != std::string::npos)
{
auto digits = name.substr(b, e);
int n = std::stoi(digits);
}
else
{
// Error handling
}
For anything more complicated I would use regex.
How about:
#include <iostream>
#include <string>
int main()
{
const std::string test("root75/obj_43.dat");
int number;
// validate input:
const auto index = test.find("obj_");
if(index != std::string::npos)
{
number = std::stoi(test.substr(index+4));
std::cout << "number: " << number << ".\n";
}
else
std::cout << "Input validation failed.\n";
}
Live demo here. Includes (very) basic input validation (e.g. it will fail if the string contains multiple obj_), variable length numbers at the end, or even more stuff following it (adjust the substr call accordingly) and you can add a second argument to std::stoi to make sure it didn't fail for some reason.
Here's another option
//your code:
std::string::size_type const cpos = name.find("obj_");
assert(std::string::npos != cpos);
//my code starts here:
int n;
std::stringstream sin(name.substr(cpos+4));
sin>>n;
Dirt simple method, though probably pretty inefficient, and doesn't take advantage of the STL:
(Note that I didn't try to compile this)
unsigned GetFileNumber(std::string &s)
{
const std::string extension = ".dat";
/// get starting position - first character to the left of the file extension
/// in a real implementation, you'd want to verify that the string actually contains
/// the correct extension.
int i = (int)(s.size() - extension.size() - 1);
unsigned sum = 0;
int tensMultiplier = 1;
while (i >= 0)
{
/// get the integer value of this digit - subtract (int)'0' rather than
/// using the ASCII code of `0` directly for clarity. Optimizer converts
/// it to a literal immediate at compile time, anyway.
int digit = s[i] - (int)'0';
/// if this is a valid numeric character
if (digit >= 0 && digit <= 9)
{
/// add the digit's value, adjusted for it's place within the numeric
/// substring, to the accumulator
sum += digit * tensMultiplier;
/// set the tens place multiplier for the next digit to the left.
tensMultiplier *= 10;
}
else
{
break;
}
i--;
}
return sum;
}
If you need it as a string, just append the found digits to a result string rather than accumulating their values in sum.
This also assumes that .dat is the last part of your string. If not, I'd start at the end, count left until you find a numeric character, and then start the above loop. This is nice because it's O(n), but may not be as clear as the regex or find approaches.
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.