Converting String to float is only "kind of" working...?

So I'm trying to have 2-way communication between Arduino and Unity, where they transmit back and forth a list of floats. Took me forever to figure out that they're communicating just fine, but Arduino isn't fully converting the incoming String from Unity into a float!

I created a minimalist test sketch with a String named data; I tried val=atof(data) and later val=data.toFloat(). I printed the values, and it prints out a float to 2 decimal places as expected, but then I added another debug thing: I set data = "2.1" as a test, and then told it after it's done everything else to print if val == 2.1, and it returned 0 aka false!

If I straight up set val to 2.1 without messing with String-float conversion, it returns 1 aka true, but no matter what I try the converted string doesn't seem compatible with everything else despite its appearance.

Any idea what's going on here and how to do this, preferably without having to manually write a method that takes each character in the string and mathematically constructs it into a float using place values??? (Possible, but I don't wanna if I can avoid it) Thanks in advance!

I suspect you need to read https://www.cl.cam.ac.uk/teaching/1011/FPComp/floatingmath.pdf

Squirt_5432:
So I'm trying to have 2-way communication between Arduino and Unity, where they transmit back and forth a list of floats. Took me forever to figure out that they're communicating just fine, but Arduino isn't fully converting the incoming String from Unity into a float!

I created a minimalist test sketch with a String named data; I tried val=atof(data) and later val=data.toFloat(). I printed the values, and it prints out a float to 2 decimal places as expected, but then I added another debug thing: I set data = "2.1" as a test, and then told it after it's done everything else to print if val == 2.1, and it returned 0 aka false!

If I straight up set val to 2.1 without messing with String-float conversion, it returns 1 aka true, but no matter what I try the converted string doesn't seem compatible with everything else despite its appearance.

Any idea what's going on here and how to do this, preferably without having to manually write a method that takes each character in the string and mathematically constructs it into a float using place values??? (Possible, but I don't wanna if I can avoid it) Thanks in advance!

KSP serial interface?

Anyway, the Arduino IDE designers, in their infinite wisdom (?), left out floating point support for printf and scanf to save a few bytes (without even the option to enable it if desired).

Don't know which IDE you're using, but this info should help you: http://josh.to/crema/rebuilding-the-arduino-ide-to-support-sprintf-with-floats/

There's a kludge function called dtostrf which allows you to print floating point values, but I don't know if there's anything to evaluate incoming floating point (such as scanf). You may need to write yourself some helper functions.......

Krupski:
I don't know if there's anything to evaluate incoming floating point (such as scanf).

There is atof or strtod.

without even the option to enable it if desired

That is NOT true. Do some research. It's not all that difficult.

There's a kludge function called dtostrf

That's like calling setup() and loop() kludges because the IDE requires you to use them.

Not sure about your other issues, but it's generally not a good idea to test equality on floats.

Squirt_5432:
if val == 2.1, and it returned 0 aka false!

You talk about converting a String to a float using atof(). You do realize that String (a C++ class object) and string (a C char array) are two different things, right? Assuming you can directly mix the two is kind of an oil/gasoline thing.

Thanks all for the replies; I'll check out links when I get the chance. I do know the difference between Strings and char arrays, neither of them worked with atof() or toFloat(). I also know I usually shouldn't test for equality on floats, but that was just a temporary test and won't be used in the actual project (I think). As far as the whole changing the IDE thing, sounds like writing my own workaround methods might actually be easier...?

Squirt_5432:
char arrays, neither of them worked with atof()

void setup() {
  char buff[] = "123.34";
  Serial.begin(250000);
  float value = atof(buff);
  Serial.println(value);
}
void loop() {}
123.34

Squirt_5432:
Thanks all for the replies; I'll check out links when I get the chance. I do know the difference between Strings and char arrays, neither of them worked with atof() or toFloat().

atof() works fine for me. It does however give a compare "mismatch" when trying to do a direct float compare exactly as you got. This is not an error, just do the compare correctly (for floats) and your code will work correctly.

I also know I usually shouldn't test for equality on floats

So don't do it then. :confused:

The following code prints 2.1 and "Match".

  float x = 2.1;
  Serial. println(x);
  if (x == 2.1) Serial.println("Match");
  else Serial.println("Mismatch");

The following code prints 2.1 and "Mismatch".

 float x = atof("2.1");
  Serial. println(x);
  if (x == 2.1) Serial.println("Match");
  else Serial.println("Mismatch");

The following code prints 2.1 and "Match". This is the correct way to compare equality of floats (Though not necessarily with the given tolerance of 1e-6, that is just an example.)

 float x = atof("2.1");
  Serial. println(x);
  if (fabs(x - 2.1)< 1e-6) Serial.println("Match");
  else Serial.println("Mismatch");

So everything works as expected. As expected direct compares on floats doesn't always work.

BTW. In case anyone is wondering why there should (or could) be a difference between x=2.1 and x=atof("2.1").

Remember that with x=2.1 that value is calculated at compile time by your host computer (PC or whatever) and not by the Arduino. So that conversion (from the ascii in the source code) is probably done in double precision before being converted to the best single precision equivalent for the Arduino.

One the other hand, the run time conversion of x=atof("2.1") has to be carried out entirely in the Arduino's emulated single precision. So you must expect (the possibility at least) of greater rounding errors occurring. Remember that such a conversion involves a series of floating point operations.

stuart0:

 float x = atof("2.1");

Serial. println(x);
  if (fabs(x - 2.1)< 1e-6) Serial.println("Match");
  else Serial.println("Mismatch");

I suspected it might be something like this... So, what I'm understanding is the atof() function should still work if what I want to do is just apply the values?

So, what I'm understanding is the atof() function should still work if what I want to do is just apply the values?

That depends on what "apply the values" means. Nothing that the Arduino does natively uses floats.

Squirt_5432:
I suspected it might be something like this... So, what I'm understanding is the atof() function should still work if what I want to do is just apply the values?

Yes, if you use the values more carefully it will work. Floating point representations have limited precision, and because they're stored in another base (base 2) then even simple looking numbers like the "2.1" of your example are only approximate. The best possible single precision representation of 2.1 for example comes out at about 2.099999905 when converted from binary.

As I said above, 'x=2.1' will probably give the best possible single precision representation (but still not exact) for that number because it's calculated at compile time. Whereas 'x=atof("2.1")' involves a series of emulated single precision floating point calculations at run time so it could well be less precise.

@stuart0, you are wrong about why the float x = 2.1 assignment and the subsequent comparison to 2.1 works, while the conversion of the string to a value, using atof() and then comparing the results to 2.1 fails.

The reason that the assignment then comparison "works" is that the compiler can see that value assigned to x and the value in the conditional expression are the same value, so the code that it generates does not actually perform a test at run time.

With the call to atof() involved, the compiler does not know what atof() does, so it can not tell that the result will be the value 2.1, so the code that it generates actually needs to perform the test at run time.

PaulS:
@stuart0, you are wrong about why the float x = 2.1 assignment and the subsequent comparison to 2.1 works, while the conversion of the string to a value, using atof() and then comparing the results to 2.1 fails.

The reason that the assignment then comparison "works" is that the compiler can see that value assigned to x and the value in the conditional expression are the same value, so the code that it generates does not actually perform a test at run time.

That is just not true PaulS. Whether or not the compiler optimizes that code away, the result is the same in either case. It is logically incorrect to attribute the success or failure of a program to whether or not the compiler makes certain optimizations in such cases where the result is the same either way.

It is very easy to rewrite that code in such a way that the compiler cannot optimize it, and the result is exactly the same as before.

The following code prints 2.1 and true (if you enter a number 10 or greater).

  float x = 2.1;
  while (!Serial.available());
  k = Serial.parseInt();
  if (k<10) x = 3.0;
  Serial. println(x);
  if (x==2.1) Serial.println("True");
  else Serial.println("False");

PaulS:
(1) That is NOT true. Do some research. It's not all that difficult.
(2) That's like calling setup() and loop() kludges because the IDE requires you to use them.

(1) Sure, IF I used the new IDE I could edit "platform.txt" to get the support... then edit it again when I didn't need it.... ad-nauseaum. What I'm saying is that such an important option should be a checkbox in Preferences, not an afterthought.

(2) The IDE does not require setup() and loop(). Simply write your code as int main (void) and it works... no kludges needed.