1

Updated:

I have been trying all methods I could find in stackoverflow, and still could not find a solution.

My point is, I have a string "23.46" and would like to transfer it to double or float anyway. This is in order to be used in another library.

But no matter how I trancate,floor,*100,add,round, it always gives me 24.4600000001 or something like this.

I know there are some precision issue while transfer. But I do need a way to give me a number that double d = 24.46 precisely.

============================================================================== I have many string values and some of them are double with converted precision as below:

char pt[100];
sprintf(pt, "%.2lf", i);
return string(pt);

Now on the other side of the code, I need to convert the strings back to double, but I tried strtod and atof with precision loss.

My questions are:

  1. what is the good way to check if a string could be a double?

  2. how to convert string back to double with given precision? I only need it to %.2lf be like:

0.21, 35.45, ...

Thanks so much!

shenmufeng
  • 67
  • 1
  • 7
  • 2
    Reading [Is floating point math broken?](https://stackoverflow.com/questions/588004/is-floating-point-math-broken) is a good first step. And realizing that typical ieee754 doubles have about 16 digits of precision when displayed in base 10. – Shawn May 27 '20 at 09:21
  • What's the stringstream there for? Is it used? Do you use raw C strings or std::string? – Lundin May 27 '20 at 09:30
  • Why? Why not just keep everything as `double`? The round-trip between `double` and `string` isn't guaranteed to preserve the value identically. So why do it at all? – user207421 May 27 '20 at 09:50
  • You might be interested by [JSON](http://json.org) and you should carefully read documentation of [snprintf(3)](https://man7.org/linux/man-pages/man3/snprintf.3.html). Do notice that C and C++ are different languages. See [here](https://en.cppreference.com/w/). Be also aware of [locale(7)](https://man7.org/linux/man-pages/man7/locale.7.html) – Basile Starynkevitch May 27 '20 at 10:09
  • *"I do need a way to give me a number that double d = 24.46 precisely."* You can't, because 24.46 can't be *exactly* represented using a `double` (https://godbolt.org/z/i_n2nK). You need a [decimal](https://stackoverflow.com/questions/14096026/c-decimal-data-types) data type. If you want to transfer exact doubles you can use the [`std::hexfloat`](https://en.cppreference.com/w/cpp/io/manip/fixed) modifier. – Bob__ May 28 '20 at 07:48

1 Answers1

-1

Given that you say that using std::strtod is not giving you a solution to the problem, you can use stringstreams to parse strings as doubles, you can also use its flags to assert if the contents of the string are convertible.

Here is an example with some conversions back and forth, and with checks to see if the whole string (not just some digits in it), is parseable as double:

Live demo

#include <iostream>
#include <sstream>
#include <iomanip>

int main()
{
    std::string str = "23.4k7"; //string with a character in the middle
    std::istringstream ss(str); //put string in a stream
    double num;
    ss >> num; //convert string to double

    if(!ss.fail() && ss.eof()) { //check if the whole string is parseable
        std::cout << "is parseable" << std::endl;
    }
    else {
         std::cout << "is not parseable";
         return EXIT_FAILURE;
    }

    std::stringstream to_num;
    to_num  << std::fixed << std::setprecision(2) << num; //double to string 2 decimal places
    std::cout << to_num.str();

    to_num >> num; //final conversion to double
}

Since the string has an alphabetic character in it, this will output:

is not parseable

But if you use a valid number it will output the converted value:

string str:

234.2345

Output:

is parseable
234.23

Note that you could use

Live demo

if(ss >> num)
    std::cout << "is parseable";

This, however, has a weakness, it will still parse if you have for instance 123.45rt56, 123.45 will be parsed, the rest will be discarded, the way it is in the sample code, if the string has any character, it will return an error. You can choose the more appropriate way for your needs.

anastaciu
  • 23,467
  • 7
  • 28
  • 53
  • `if(!ss.fail() && ss.eof())` should be replaced with `if(!ss)`. – Maxim Egorushkin May 27 '20 at 10:10
  • @MaximEgorushkin, it doesn't work the result is always `is not convertible`. – anastaciu May 27 '20 at 10:30
  • @MaximEgorushkin, the same with `if(ss)`, it will convert till it finds anything other than a digit, then it stops. – anastaciu May 27 '20 at 10:38
  • @MaximEgorushkin, in fact `if(!ss)` only returns true if the first character in the string is non digit. – anastaciu May 27 '20 at 10:40
  • @MaximEgorushkin, sorry for the chained comments but I'm pretty sure this is the way to go, [see this answer](https://stackoverflow.com/a/29169469/6865932) – anastaciu May 27 '20 at 10:44
  • The canonical way is `if(ss >> num) // parsed successfully`. But using `ifstream` is totally unnecessary here, one should use `std::stod` or `std::strtod`. – Maxim Egorushkin May 27 '20 at 10:46
  • @MaximEgorushkin, yes I know, and I use it often, but this will still return `true` if you have for instance `123.45trf34`, the converted value will be `123.45` the rest will be discarded. The way to check if the whole string is convertible, that is, that there are no non digit characters in the whole string is like this, of course there are other ways, but using stringstreams this is the way. – anastaciu May 27 '20 at 10:48
  • @MaximEgorushkin, note that the OP states that `strtod` was used and it didn't achieve the intended goal. – anastaciu May 27 '20 at 11:00