tags:

views:

229

answers:

9

I'm wondering what sort of algorithm could be used to take something like "4.72" into a float data type, equal to

float x = 4.72;
A: 

The atof() function can be helpful. http://www.cplusplus.com/reference/clibrary/cstdlib/atof/

Xavier V.
I'd strongly recommend `strtod()` over `atof()` since you have better chance of finding out that something went wrong when you get unexpected input with `strtod()`.
RBerteig
That's totally right. I agree.
Xavier V.
+3  A: 

For C strtod() and C99 friends strtof() and strtold() (description on same link) already have that algorithm implemented.

If you are having problems writing your own, post your code and specific questions about it.

pmg
`strtof` instead of `strtod`, since the questioner wants a `float` instead of a `double`.
Stephen Canon
Right Stephen, thanks (but strtof and strtold are "new"). Post edited.
pmg
Using `strtod` and converting the result to `float` (implicitly or with a cast) is just as good and a lot more portable than `strtof`.
R..
@R..: It isn't quite "just as good"; it has the potential to incur a double-rounding, making it very slightly less accurate (though if you're using `float`, you probably don't care about this), and can be much slower (depending on your c library). For most uses, these concerns are trumped by portability, but not for all uses.
Stephen Canon
+8  A: 

scanf, operator>> for istreams, and strtof would be the obvious choices.

There is also atof, but, like atoi, it lacks a way to tell you there was an error in the input, so it's generally best to avoid both.

Jerry Coffin
+1 for `strtof`.
Stephen Canon
+1  A: 

You can use boost:lexical_cast

http://www.boost.org/doc/libs/1_44_0/libs/conversion/lexical_cast.htm

David
+7  A: 

For C++ you can use boost::lexical_cast:

   std::string str( "4.72" );
   float x = boost::lexical_cast< float >( str );

For C you can use sscanf:

   char str[]= "4.72";
   float x;
   sscanf( str, "%f", &x );
Eugen Constantin Dinca
+2  A: 

For C++ This is the algorithm I use:

bool FromString(const string& str, double& number) {

    std::istringstream i(str);

    if (!(i >> number)) {
        // Number conversion failed
        return false;
    }

    return true;
}

I used atof() in the past for the conversion, but I found this problematic because if no valid conversion can be made, it will return (0.0). So, you would not know if it failed and returned zero, or if the string actually had "0" in it.

SuperJames
A: 

From cplusplus.com: "stringstream provides an interface to manipulate strings as if they were input/output streams."

You can initialize a stringstream with your string then read a float from the stringstream using operator>> just like you would with cin.

Here is an example:

#include<iostream>
#include<string>
#include<sstream>
using namespace std;

int main() {
  string s = "4.72";
  stringstream sstrm(s);
  float x;
  sstrm >> x;
  cout << x << endl;
}
JohnPS
+1  A: 

I assume you want an actual algorithm, not a library function that already does it. I don't have time to write and test actual code, but here is what I would do:

  1. Initialize a float that will be used as an accumulator to 0.
  2. Figure out where the decimal place is in the string, this will let you know what "column" each of the digits is (i.e. 100s, 10s, 1s, 1/10ths, etc).
  3. Start at the beginning of the string.
  4. Take that digit, convert to an int (trivially done by subtracting 0x30 from the ASCII value)
  5. Multiply the value by the place column (for the first digit in your example, that would be 4 * 1 == 4, for the next digit 7 * 0.1 == 0.7).
  6. Add the result to the accumulator
  7. Repeat from step 4 for each remaining digit.
  8. The accumulator now contains your result.

Due to the roundoff of converting between base 10 and base 2 in every iteration of this loop, the result you get from this algorithm may not be the closest possible binary representation to the original value. I don't really know of a good way to improve it though... perhaps someone else can chime in with that.

rmeador
Don't forget to handle numbers like `-1.2e-3`. This conversion is fraught with subtleties and edge cases. It is by no means easy to get right. If you must implement it yourself, test the crap out of it at every edge case you can dream up. And then worry. And don't forget about NaN and INF cases either.
RBerteig
@RBerteig: if this is my string conversion function, I can simply declare negative numbers and scientific notation to be invalid ;) But really, yes, it needs to be more complex than what I wrote. I figured the OP was just trying to wrap is head around how such a conversion is possible.
rmeador
@rmeador, Of course you can decide on reasonable bounds for what should be handled in order to simplify the problem. I just thought I'd add the reminder that converting text to floating point might need to handle cases more complicate than "4.72". One implementation of C89 compliant `strtod()` I've seen is about 80 lines of dense and hardly commented code, for example.
RBerteig
@rmeador, I believe my upvote just pushed you over 10K. Congrats.
RBerteig
@RBerteig: it did indeed. Thank you :)
rmeador
+1  A: 

As you've asked for an algorithm, not a method, here is my explanation for a simple algorithm (and an implementation in C):

  1. Initialize 4 integer variables, one for the value before dot, one for the after part, one for the power of the mantissa, one for the sign. Let's say, f, m, d, sign = 1.
  2. First look for + or - sign at the beginning. If there are no sign characters or + sign then continue. If the first character is -, then sign = -1.
  3. Then, read an integer value into f until a . or NULL character.
  4. If you end up with a dot character, then start reading the mantissa part as in the previous step into m. But this time also multiply d by 10 with each digit.
  5. In the end, return sign*(f + (float) m/d). The casting makes sure the division is done in floating-points and the type of the expression is float.

I guess, reading the code might be easier. So here is the code:

float atof(char *s)
{
    int f, m, sign, d=1;
    f = m = 0;

    sign = (s[0] == '-') ? -1 : 1;
    if (s[0] == '-' || s[0] == '+') s++;

    for (; *s != '.' && *s; s++) {
            f = (*s-'0') + f*10;
    }
    if (*s == '.')
            for (++s; *s; s++) {
                    m = (*s-'0') + m*10;
                    d *= 10;
            }
    return sign*(f + (float)m/d);
}
Serkan
Nice and clear explanation with source for the simple part. Don't forget to handle `-1e-6`, `.3e+5`, `+2.e-4` and similar, or specify explicitly that you don't allow this form. Consider handling malformed numbers better (try "ABC" as input). Also consider handling NaN and INF. C99 requires that `scanf()` be capable of exactly recovering a value printed by `printf()`, even NaN and INF. C89 does not, but its `strtod()` does handle the `E` notation.
RBerteig