patterncppMinor
Converting a double to a std::string without scientific notation
Viewed 0 times
stdwithoutscientificdoublenotationconvertingstring
Problem
Problem
I need to convert a double to a
Using
I am looking for improvements in robustness or clarity/simplicity in the implementation.
Details/Elaboration (addition)
I realize that the conversion between
I have attempted solutions involving
```
#include
#include
#include
#include
static std::string
to_string( double d )
{
char s[64];
sprintf( s, "%g", d );
char* e = std::strchr( s, 'e' );
if ( e==nullptr )
return s;
char* p = std::strchr( s, '.' );
// eg 2.4e-07
int exp = atoi( e+1 ); // -7
int extra = 0;
// snip period eg 24
if ( p!=nullptr ) {
extra = e-p-1;
memmove( p, p + 1, strlen(s) - (p-s));
}
int coeff = atoi( s );
// std::cerr << "DEBUG = " << s << " coeff = " << coeff << " exp = " << exp << "\n";
if ( exp<0 ) {
sprintf( s, "0.%0*d", -exp+extra, coeff);
return s;
}
// eg 7.3e+09
I need to convert a double to a
std::string which contains no scientific notation and no unnecessary fillers.Using
std::fixed with iostream yielded ugly results - extra fillers (0's). The closest working code I could get was to using sprintf("%g",...) and then dealing with exponentials, should they appear (beyond 6 places).I am looking for improvements in robustness or clarity/simplicity in the implementation.
Details/Elaboration (addition)
I realize that the conversion between
double and std::string and back can be expected to be lossy. So I am not looking for an implementation that works for all cases. For example, I don't expect to be able to convert "0.2000" to double and then get the same back (getting "0.2" back would be fine). Similarly with "+0.2000": getting "0.2" back is ok.I have attempted solutions involving
sprintf("%g",...), but the result is automatic conversion to scientific notation when we go beyond six significant digits. There appears to be no way to configure that setting. Alternatively, sprintf("%f",...) requires that we manually set or calculate the exact precision that we want - which I don't know how to do if that information is lost inside a double. Note that what I have found here seems to equally apply to most simple solutions involving std::iostream and std::fixed.```
#include
#include
#include
#include
static std::string
to_string( double d )
{
char s[64];
sprintf( s, "%g", d );
char* e = std::strchr( s, 'e' );
if ( e==nullptr )
return s;
char* p = std::strchr( s, '.' );
// eg 2.4e-07
int exp = atoi( e+1 ); // -7
int extra = 0;
// snip period eg 24
if ( p!=nullptr ) {
extra = e-p-1;
memmove( p, p + 1, strlen(s) - (p-s));
}
int coeff = atoi( s );
// std::cerr << "DEBUG = " << s << " coeff = " << coeff << " exp = " << exp << "\n";
if ( exp<0 ) {
sprintf( s, "0.%0*d", -exp+extra, coeff);
return s;
}
// eg 7.3e+09
Solution
As mentioned in the comments of the other answer, your decimal floating point numbers are stored as binary and rarely have finite representations.
If you simply want code that passes your test I would mention that it is much easier to remove padding from
If you simply want code that passes your test I would mention that it is much easier to remove padding from
std::fixed than it is to expand the scientific notation.#include
#include
#include
#include
#include
#include
#include
#include
static std::string to_string(double d)
{
std::ostringstream oss;
oss.precision(std::numeric_limits::digits10);
oss << std::fixed << d;
std::string str = oss.str();
// Remove padding
// This must be done in two steps because of numbers like 700.00
std::size_t pos1 = str.find_last_not_of("0");
if(pos1 != std::string::npos)
str.erase(pos1+1);
std::size_t pos2 = str.find_last_not_of(".");
if(pos2 != std::string::npos)
str.erase(pos2+1);
return str;
}
static void test_string_conversion(const std::string& str)
{
assert( to_string( atof(str.c_str()) ) == str );
}
int main(void)
{
test_string_conversion( "1.5203" );
test_string_conversion( "-1.025" );
test_string_conversion( "3.1415" );
test_string_conversion( "-38271" );
test_string_conversion( "0.8382" );
test_string_conversion( "-0.8382" );
test_string_conversion( "7" );
test_string_conversion( "-3" );
test_string_conversion( "0.0000024" );
test_string_conversion( "7300000000" );
}Code Snippets
#include <iostream>
#include <string>
#include <sstream>
#include <limits>
#include <iomanip>
#include <cstdlib>
#include <cassert>
#include <cstddef>
static std::string to_string(double d)
{
std::ostringstream oss;
oss.precision(std::numeric_limits<double>::digits10);
oss << std::fixed << d;
std::string str = oss.str();
// Remove padding
// This must be done in two steps because of numbers like 700.00
std::size_t pos1 = str.find_last_not_of("0");
if(pos1 != std::string::npos)
str.erase(pos1+1);
std::size_t pos2 = str.find_last_not_of(".");
if(pos2 != std::string::npos)
str.erase(pos2+1);
return str;
}
static void test_string_conversion(const std::string& str)
{
assert( to_string( atof(str.c_str()) ) == str );
}
int main(void)
{
test_string_conversion( "1.5203" );
test_string_conversion( "-1.025" );
test_string_conversion( "3.1415" );
test_string_conversion( "-38271" );
test_string_conversion( "0.8382" );
test_string_conversion( "-0.8382" );
test_string_conversion( "7" );
test_string_conversion( "-3" );
test_string_conversion( "0.0000024" );
test_string_conversion( "7300000000" );
}Context
StackExchange Code Review Q#90565, answer score: 4
Revisions (0)
No revisions yet.