Reputation:
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.
Upvotes: 4
Views: 628
Reputation: 28251
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;
}
Upvotes: 1
Reputation: 20513
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();
}
Upvotes: 1