0

It's C++ code written in Visual Studio 2015. It's something as below,

LPSTR *endPtr;
string strDouble = "0.03456";
double valDouble = strtod(strDouble.c_str(), &endPtr);

Now the output in valDouble is "0.0345555566", something like that.

I want the value in valDouble to be exactly "0.03456".

Basically the value of "0.0345555566" needs to be rounded to say "0.03456".

Is there a way it can be achieved?

BTW, the value in strDouble changes all the time. So it's not possible to set precision to say 5 or something like that. Below are few examples that goes in to strDouble.

string strDouble = "0.1889";
string strDouble = "0.00883342";
string strDouble = "0.2111907";
string strDouble = "3.0045";
string strDouble = "1.45";
  • https://en.cppreference.com/w/cpp/string/basic_string/stof , https://en.cppreference.com/w/cpp/numeric/math/round – Jesper Juhl Mar 17 '19 at 13:18
  • 6
    Relevant: [Is floating point math broken?](https://stackoverflow.com/q/588004/10147399) – Aykhan Hagverdili Mar 17 '19 at 13:19
  • Do you want the number of decimal places after dot to be a percent of something? – HariUserX Mar 17 '19 at 13:22
  • 2
    It's because you cannot EXACTLY represent `0.03456` in the floating point system. – Yashas Mar 17 '19 at 13:23
  • 1
    `valDouble` wouldn't have a the value 0.0345555566, I don't know where you got that number. It might have something that's very close to 0.03456 such as 0.03455999999. That's because floating point can't hold decimal numbers accurately. – interjay Mar 17 '19 at 13:44
  • 2
    @Gox May I suggest, that you use cppreference.com rather than cplusplus.com as a reference site? The latter is full of inaccuracies, plain errors and bad advice. – Jesper Juhl Mar 17 '19 at 14:41
  • Just one suggestion: use std:stod() instead of strtod() in C++ (later one is C funciton) https://en.cppreference.com/w/cpp/string/basic_string/stof –  Mar 17 '19 at 14:51

3 Answers3

1

I want the value in valDouble to be exactly "0.03456".

That's not possible, unless you target a system whose double floating point representation can represent that number.

There exists no representation for 0.03456 in the ubiquitous IEEE 754 binary64 standard which your CPU probably uses. The closest representable number is 3.45600000000000004418687638008E-2. That's the number that you should get whether you use strtod, stod or a character stream to convert the string.

Is there a way it can be achieved?

In order to represent 0.03456 exactly on a system whose floating point cannot represent that number, you must use integers to represent the number. You can implement arbitrary precision arithmetic, fixed-point arithmetic or a decimal floating point using integers.

Basically the value ... needs to be rounded to say "0.03456".

You can round the output when you convert the non-exact float into a string:

std::cout << std::setprecision(4) << 0.03456;

BTW, the value in strDouble changes all the time. So it's not possible to set precision to say 5 or something like that.

Then you have to record the number of significant digits in the input string in order to use the same precision in output.

Here's an example function for that purpose:

template<class Range>
auto get_precision(const Range& r)
{
    auto is_significant = [](auto c) {
        return std::isdigit(c) && c != '0';
    };
    auto first = std::find_if(std:: begin(r), std:: end(r), is_significant);
    auto last  = std::find_if(std::rbegin(r), std::rend(r), is_significant).base();
    return std::count_if(first, last, [](auto c) {
        return std::isdigit(c);
    });
}

// demo
std::cout << get_precision("0.03456"); // 4
eerorika
  • 232,697
  • 12
  • 197
  • 326
0

Assuming that you want the number of digits after decimal point as some percent of the total number of digits after the decimal, you could do something like,

  1. Calculate the number of digits after decimal point. Let it be n
  2. Now convert the string to decimal just like you are doing. Let this be d
  3. Now if you want 50% of the decimal places to be retained, you could do use an old trick,

double d_new = round(d * pow(10.0, 5)) / pow(10.0, 5). Assuming precision till 5 digits.

Note: Unlike the other answers, here you are rounding the original decimal itself. Not just printing the rounded decimal to stdout.

Example:

#include<stdio.h>
#include<cmath>

int main(){
    double a = 0.0345555566;
    double b = 0.00883342;
    double c = 0.2111907;

    double a_new = round(a * pow(10.0, 5)) / pow(10.0, 5);
    double b_new = round(b * pow(10.0, 4)) / pow(10.0, 4);
    double c_new = round(c * pow(10.0, 3)) / pow(10.0, 3);

    printf("%.10f\n", a_new);
    printf("%.10f\n", b_new);
    printf("%.10f\n", c_new);
}

See the 50% precision

Results:

0.0345600000

0.0088000000

0.2110000000

HariUserX
  • 1,341
  • 1
  • 9
  • 17
  • why on earth do you use pow for a constant power of 10? Just use compile time literals like 1e3, 1e4, 1e5 – phuclv Mar 17 '19 at 14:08
  • What I meant was 5 is half of the number of decimal digits after dot in `a`. Suppose the half is `n`. He/She could do `pow(10.0, n)`. Its just an example and I assumed that he/she needed to retain some 50% of digits after dot rounded up. – HariUserX Mar 17 '19 at 14:11
-1

Use string stream instead of strtod:

#include <iostream>
#include <sstream>

double convert(std::string string) {
    std::stringstream s(string);
    double ret = 0;
    s >> ret;
    return ret;
}

int main() {
    std::cerr << convert("0.03456") << std::endl;
    std::cerr << convert("0.1889") << std::endl;
    std::cerr << convert("0.00883342") << std::endl;
    std::cerr << convert("0.2111907") << std::endl;
    std::cerr << convert("3.0045") << std::endl;
    std::cerr << convert("1.45") << std::endl;
    return 0;

}

On my system, this gives:

0.03456
0.1889
0.00883342
0.211191
3.0045
1.45

As some have pointed out in the comments, not all numbers can be represented with doubles. But most of the ones you listed can be.

Jan Müller
  • 413
  • 3
  • 18
  • 2
    Using `strtod` would also give the same output. What matters is how you output it, not how you convert to `double` (which will always be inaccurate). – interjay Mar 17 '19 at 13:46