Strange math situation with casting in 328 and ATtiny85

The crux of the problem is further down, if you want to skip the background.

Background: I have code that I'm compiling for both an UNO (328) and an ATTiny85, in which I define a version number, and extract the integer and decimal parts to flash on two different LEDs on boot. E.g., version 4.1 flashes LED1 four times, and LED2 once. For some version numbers (e.g., 0.9, 1.1) this works fine. For others, it does not (e.g., 1.3, 3.1, 3.6, ...) There does not seem to be a pattern. I have stripped away all the code to the bare bones, and here's what I've found. I know this makes no sense, so I'd be grateful for someone to point out where I'm missing something. I'm pretty capable, and I've thought a lot about it and can't figure it out, so thanks for your time. I've also had my brother verify the same behaviour on his UNO, different computer, different city.

Crux: the following is standalone code, compiled in VSC Arduino for UNO (328) and uploaded via USB cable. It has also been tested compiled for an ATtiny85 and flashed via a USBasp. This is the entire code, followed by the output. I know there's a lot of redundancy; I was testing things grasping for straws.

#define VERSION 3.1

void setup() {
  Serial.begin(19200);

  byte decimal_b = (byte)((VERSION-(int)VERSION)*10);
  int decimal_i = (int)((VERSION-(int)VERSION)*10);
  float decimal_f = (float)((VERSION-(int)VERSION)*10);

  Serial.println((VERSION-(int)VERSION)*10);
  Serial.println(decimal_b);
  Serial.println(decimal_i);
  Serial.println(decimal_f);
  Serial.println((int)decimal_f);
  Serial.println((byte)decimal_f);
}

void loop() {}

When VERSION is 3.1:

[Info] Opened the serial port - COM12
1.00
0
0
1.00
0
0

When VERSION is 3.2:

[Info] Opened the serial port - COM12
2.00
2
2
2.00
2
2

I tested version numbers 1.X and 3.X where X=0..9. There seems to be no pattern. The following "work" in that the (int) cast is as expected (2 for version 3.2):

1.1, 1.2, 1.5, 1.6, 1.7, 1.9, 1.0
3.2, 3.4, 3.5, 3.7, 3.9, 3.0

The others do not work, in that the decimal is one less than expected given the version number.

As you know, when you convert a float to an int, the numbers after the decimal point are truncated, right?

Now, do the following code and see for yourself. :grin:

void setup()
{
  Serial.begin(9600);
  Serial.println(3.0, 7);
  Serial.println(3.1, 7);
  Serial.println(3.2, 7);
  Serial.println(3.3, 7);
  Serial.println(3.4, 7);
  Serial.println(3.5, 7);
  Serial.println(3.6, 7);
  Serial.println(3.7, 7);
  Serial.println(3.8, 7);
  Serial.println(3.9, 7);
  Serial.println(4.0, 7);
}
void loop() {}

This is the truth. :roll_eyes:

1 Like

Floating point numbers are never exactly accurate. You will be far better of either using defines for version major and version minor or use a c-string.

#define VERSION_MAJOR 3
#define VERSION_MINOR 1
//or
#define VERSION "3.1"
//or
const char *version = "3.1";
1 Like

casting a float to an int is not the same as converting a float to an int using int()

((VERSION- int (VERSION))*10);

Wow. I'll have to do some digging to learn why it's different for different integer parts, but yes, these are exactly the ones that didn't work in my test. Thank you!

1.0000000
1.1000000
1.2000000
1.2999999
1.3999999
1.5000000
1.6000000
1.7000000
1.7999999
1.9000000
3.0000000
3.0999999
3.2000000
3.2999999
3.4000000
3.5000000
3.5999999
3.7000000
3.7999999
3.9000000

This actually does not change anything, probably because it's the actual float representation that is imprecise as demonstrated by @chrisknightley

Thanks @sterretje. How would I then extract the values 3 and 1 from the string #define or char version?

[and I just realized these replies aren't threaded and noticed the note about replying to several posts at once...apologies...I won't do this again!]

Something in this line

const char *VERSION = "3.1";

void setup()
{
  Serial.begin(57600);

  int vMaj;
  int vMin;

  char *ptr = strchr(VERSION, '.');
  if (ptr == NULL)
  {
    vMaj = vMin = -1;
  }
  else
  {
    *ptr = '\0';
    vMaj = atoi(VERSION);
    vMin = atoi(ptr + 1);
  }
  Serial.print("major = "); Serial.println(vMaj);
  Serial.print("minor = "); Serial.println(vMin);
}

void loop()
{
}

Please note that it mangles the original VERSION.

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