2

My code:

   // Convert SATOSHIS to BITCOIN
    static double SATOSHI2BTC(const uint64_t& value)
    {
        return static_cast<double>(static_cast<double>(value)/static_cast<double>(100000000));
    }

    double dVal = CQuantUtils::SATOSHI2BTC(1033468);
    printf("%f\n", dVal);
  printf("%s\n", std::to_string(dVal).data());

Google output: 0.01033468

Program output: 0.010335 both for printf and std::to_string

Debugger output: 0.01033468

Do printf and std::to_string round the number? How do I get a string with the proper value?

MikeCAT
  • 73,922
  • 11
  • 45
  • 70
PeeS
  • 1,164
  • 3
  • 20
  • 43
  • the default precision for `to_string()` and (I guess `printf`) is 6 decimal places (see this: http://stackoverflow.com/questions/14520309/the-precision-of-stdto-stringdouble and http://stackoverflow.com/questions/16605967/set-precision-of-stdto-string-when-converting-floating-point-values) – Nim Nov 02 '15 at 12:33
  • 1
    Neither the "google"(wtf?) nor the debugger output makes any sense. – Karoly Horvath Nov 02 '15 at 12:33
  • Sorry, wrong copy/paste. updated. The Google output is what you get if you use the google calc. Thanks for the answer @Nim – PeeS Nov 02 '15 at 12:35
  • Why?! Just leave it as integer Satoshis. Whatever problem you hope to solve by converting it to a `double`, solve some other (better!) way. – David Schwartz Nov 02 '15 at 12:48
  • @DavidSchwartz I have this as SATOSHI for internal use, but i need to present the data to the Exchange by providing a double value. if go with what i have, then i will be ordering MORE than i am asking for (due to rounding in std::to_string). – PeeS Nov 02 '15 at 12:49
  • @PeeS How do you provide a `double` to the exchange? Does it have some binary API that accepts a `double`?! (If so, tell us which exchange it is so we can all avoid it!) – David Schwartz Nov 02 '15 at 12:50
  • @DavidSchwartz My bad, you provide a STRING with a value for the order, but they go in BTC not SATOSHI so you have to convert your SATOSHI to BTC and have a proper number in the string. – PeeS Nov 02 '15 at 12:51
  • Ahh, okay. So write a routine to convert the integer to the correct string. Put the decimal point in the right place adding leading zeroes if needed. No reason to mess with doubles and rounding and other things that are easy to get wrong. – David Schwartz Nov 02 '15 at 12:52

3 Answers3

1

The std::to_string function uses the same notation as with printf:

7,8) Converts a floating point value to a string with the same content as what std::sprintf(buf, "%f", value) would produce for sufficiently large buf.

The printf documentation shows:

Precision specifies the minimum number of digits to appear after the decimal point character. The default precision is 6.

You can use %.32f to indicate how many decimals you want (e.g. 32):

printf("%.32f\n", dVal);

I cannot find a way to change the number of decimals with to_string, but you could print the value to a string with sprintf:

char buffer [100];
sprintf (buffer, "%.32f", dVal);
printf ("%s\n",buffer);

And if you want a std::string:

std::string strVal(buffer);
agold
  • 6,140
  • 9
  • 38
  • 54
1

It's a little tricky with the field width

#include <iostream>
#include <iomanip>
#include <cmath>
#include <string>
#include <sstream>
#include <limits>

#define INV_SCALE 100000000

static const int      WIDTH   = std::ceil(
                                    std::log10(std::numeric_limits<uint64_t>::max())
                                ) + 1 /* for the decimal dot */;
static const uint64_t INPUT   = 1033468;
static const double   DIVISOR = double(INV_SCALE);
static const int      PREC    = std::ceil(std::log10(DIVISOR));

static const double   DAVIDS_SAMPLE = 1000000.000033;

namespace {
std::string to_string(double d, int prec) {
    std::stringstream s;
    s << std::fixed
      << std::setw(WIDTH)
      << std::setprecision(prec) 
      << d;
    // find where the width padding ends    
    auto start = s.str().find_first_not_of(" ");
    // and trim it left on return
    return start != std::string::npos ? 
                    &(s.str().c_str()[start]) : "" ;
}
}

int main() {
    for (auto& s : 
            {to_string(INPUT/DIVISOR, PREC), to_string(DAVIDS_SAMPLE, 6)} 
        ) std::cout << s << std::endl;

    return /*EXIT_SUCCESS*/ 0;
}

output:

0.01033468
1000000.000033
decltype_auto
  • 1,706
  • 10
  • 19
-1

Thanks to all answers,

this made the trick:

std::stringstream ss;
ss << std::setprecision(8) << dVal;
std::string s = ss.str();
printf("ss: %s\n", s.data());

Output:

ss: 0.01033468

PeeS
  • 1,164
  • 3
  • 20
  • 43
  • This doesn't work. Try with a dVal of `1000000.000033`. Or even just [this](http://codepad.org/SZKzKC4z). – David Schwartz Nov 02 '15 at 13:05
  • incredible.. how to approach this then? – PeeS Nov 02 '15 at 13:05
  • Don't use a `double`. Why would you use a `double` for this? That makes absolutely no sense. You want to convert an integer to a string, why go through some other numeric type? That's nuts. – David Schwartz Nov 02 '15 at 13:06
  • a double is to ensure that the division of Satoshi to BTC is made in double precision. then this has to be represented as string, i don't understand what other option of doing it is possible? – PeeS Nov 02 '15 at 13:07
  • First, convert the integer to a string representing that integer. Then add leading zeroes as needed to the string. Then insert a decimal point where needed in the string. – David Schwartz Nov 02 '15 at 13:09
  • So go straight from int to string and amend the string as needed? – PeeS Nov 02 '15 at 13:10
  • Definitely. Using an intermediary numeric type is crazy unless you know for a fact that intermediary type can exactly representing the full legal range. – David Schwartz Nov 02 '15 at 13:10
  • @DavidSchwartz thanks, i am absolutely shocked that i can't go with the normal divide/to string approach.. going to try that out now. But how do i know where to put the digit and how many zeroes to add? – PeeS Nov 02 '15 at 13:12
  • It's a good thing you didn't have to learn this lesson the painful way. Yeah, never move a financial amount through a floating point type unless you are 100% confident that the floating point type can represent with sufficient accuracy the entire range of values your code might use. – David Schwartz Nov 02 '15 at 13:13
  • @DavidSchwartz i heard these stories from some people, but i was always told to hold the internal values in integers operate on them but when you need to show something to the user, you have to convert it anyway.. and the exchanges are good example, you need to pass a BTC value so you need to go from int to string in some way. okay, let me go with this - thanks very much! – PeeS Nov 02 '15 at 13:15
  • @DavidSchwartz: It's a prob with the field width; c my answer. – decltype_auto Nov 02 '15 at 13:55