Float-to- int failure - puzzlement!

Hi all.
I am using Arduino IDE and ESP32.
The code below is showing an incorrect result in the last Print statement. I suspect it is the conversion from float to int the problem.

Please can you see why is this happening?
Regards

float Vin = 1;
void setup()
{
  Serial.begin(9600);
  print_int();
}

void print_int()
{
  Vin = Vin * 0.1;        //Here Vin=0.1
  int int_1 = Vin * 1000;

  Vin = Vin * 0.1;        //Here Vin=0.01
  int int_2 = Vin * 1000;

  Vin = Vin * 0.1;        //Here Vin=0.001
  int int_3 = Vin * 1000;

  Serial.println("");
  Serial.println(int_1); //This prints 100
  Serial.println(int_2); //This prints 10
  Serial.println(int_3); //This prints 0 - It should be 1

  //Sanity check here
  Serial.println(String(Vin,3)); //Last Vin is 0.001
}

The sketch does not compile.

Remember that a float is never accurate, it is a approximation.
For example this line:

  Vin = Vin * 0.1;        //Here Vin=0.001
  int int_3 = Vin * 1000;

Has the result of 0.99999994039535522460 which is then converted to a integer.

I call this cheating:

String(Vin,3)

Try that with 20 instead of 3.

If you are using a ESP32, please use 'double'.
If you convert a floating point number to a integer, use rounding.
Try to keep floating point numbers as floating point numbers. I prefer to use them for weight, length, speed, and so on. If you print a floating point number to the Serial Monitor, then use the second parameter to print more digits after the dot.

1 Like

Thank you very much for your reply.
And, using 'double' works. I get the correct result.
And also thank you for the explanation. Really appreciated.

Try that with 20 instead of 3.

Yes I did it. And got: 0.00099999993108212947.
That explains why was never a 1.

So, does 'double' gives the compiler more room for calculation?
Is that why now is correct?
Regards

A 'float' uses 32-bits and a 'double' uses 64-bits, so it is more accurate.
However, you should write your sketch in such a way that you get the result that you expect.

A float can be 0.99 and a double can be 0.9999, but both turn to zero when cast to a integer.

Some prefer to do calculations with (long) integers as much as possible.
I prefer to use float or double throughout the whole sketch for real world units.

Every time that you convert something, you have to know what you are doing.

Did you know that the first line of your sketch has a conversion ?

float Vin = 1;

When the compiler sees a "1", then it reads it as a integer, after that it is converted to float for 'Vin'.

This has no conversion, everything is floating point:

double Vin = 1.0;

I do that also for myself, to remind myself that 'Vin' is a floating point variable.

Vin *= 123;    // not good for me, I could forget that Vin is a floating point
Vin *= 123.0;  // good, it shows clearly that it will be a floating point calculation.
2 Likes

I completely agree with @Koepel but let me say I don't get the point. Your code seems to be a sample one, ok, but I've never had the need to do such decimal divisions except, for example, if you need to get a portion of a float value (e.g. two digits from the integer part and two from the decimals) but many alternative ways exist...

Thank you again for the insight.
I learned something new today.
My programing can only get better!
Regards.

Hi docdoc.
Just to satisfy your curiosity.
This code was to test some low voltage values from an OPAMP.
I wanted to sure how to deal with them.

regards

Ok, but I still can't get why you need that kind of divisions. If you want and still have trouble with some calculations, conversions, or output, post here real code or snippets and we'll give it a try.

As side note, strangely the first code you posted here seems to run correctly on an UNO at least under TinkerCAD where I tested it, the last print correctly gives me 1 while ESP32 gives 0.
image
I haven't investigated that but maybe it's just a matter of specific architecture float precision.

PS: there's no need to convert everything into "String" class, the last print shows exactly the same value with Serial.println(Vin,3);. :wink:

Understood.
Hoover there is no code yet.
The code you have seen was to test how my ESP32 would handle those conversions.
I am new to the ESP32.
Regards.

@Francesco2017
You can use the round() function to round to the nearest int number.
Above and equal x.5 = x+1, and below x.5 = x.

Try:

float Vin = .500001;
//--------------------------------------------------------------------
void setup()
{
  Serial.begin(9600);
  print_int();
}
//--------------------------------------------------------------------
void print_int()
{
  Vin = Vin * 0.1;        //Here Vin=0.1
  int int_1 = round(Vin * 1000);;

  Vin = Vin * 0.1;        //Here Vin=0.01
  int int_2 = round(Vin * 1000);

  Vin = Vin * 0.1;        //Here Vin=0.001
  int int_3 = round(Vin * 1000);
 
  Serial.println("");
  Serial.println(int_1); //This prints 100
  Serial.println(int_2); //This prints 10
  Serial.println(int_3); //This prints 0 - It should be 1

  //Sanity check here
  Serial.println(String(Vin,20)); //Last Vin is 0.001
}
//--------------------------------------------------------------------
void loop() {
  delay(10); // this speeds up the simulation
}

This is the description of round() function of the 'C++' language: https://cplusplus.com/reference/cmath/round/

It returns a double. It can be casted to a integer to show the conversion, or 0.5 can be added without the round functino.

int int_1 = (int) round(Vin * 1000.0);  // with round() function
int int_1 = (int) ((Vin * 1000.0) + 0.5)  // add 0.5 for rounding

For users who know the round issue: I tested the ESP32 for the return value of round (auto y = round(x); and printed the sizeof() of y and it was 8.

Got it.
Anyway, you don't only need to better understand conversions (implicit or explicit casting), but I suggest you to study also how data are stored on the specific architecture (e.g. an ESP value is stored differently from a UNO, and ESP8266 is different from ESP32), variable types constraints (e.g. on UNO float and double are the same), and, in this case, floating point storage size, precision digits, and arithmetic).

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.