From float to seperate INTs - in an elegant manner :-)

Hi, I stumbing about a bit on this one.... trying to find an elegant solution to converting a float with one decimal place to 3 sperate integer values... so:

float value = 24.8
becomes
int a = 2
int b = 4
int c = 8

Thanks for any pointers.

float f = 24.8 ;
int a ;
int b ;
int c ;

a = int(f)/10 ; b= int(f)%10 ; c=int((f-int(f))*10) ;

The fraction needs to be rounded, otherwise the .8 will set c=7
Use this:

c=int((f-int(f)+0.05)*10);

Pete

Damn, you're right ! :slight_smile: Didnt test it. The cause is that 24.8 is (probably) internally stored as 24.7999999... due to the finite limit of representing decimal fractions in binary.

Well, in an effort to keep my pride intact :wink: and to keep it "elegant" use the round function for the last bit

c=round((f[n]-int(f[n]))*10) ;

"Elegant" would also be to sprintf the float into a char[] with exactly the format you want ("%4.1f"),
and then simply get the individual digits ( - '0' )

BTW: Strictly seen (and visible on machines where float and double are different), there is no float x with ( x == 24.8 ) :wink:

"Elegant" would also be to sprintf the float into a char[] with exactly the format you want ("%4.1f"),
and then simply get the individual digits ( - '0' )

Well, that might be elegant, but it won't work. The %f format specifier does not work on the Arduino.

PaulS:
The %f format specifier does not work on the Arduino.

oops ! Thanks

Thanks folks - very nice solution with elegance built in... off to try it now :slight_smile:

do you need the individual digits as int or as a char or..... as a byte? NB a char/byte is large enough to hold one digit (byte == uint8_t)

The code sofar does not handle negative numbers (see code below), don't know if they can occur - just like numbers bigger than 99?

so I did an alternative version that

  • handle negatives
  • uses less floating point operations.
  • can easily be extended to larger numbers as it uses an array for the digits
  • has room for optimization :wink:
void setup()
{
  Serial.begin(9600);

  float f = 24.8;

  int a = int(f)/10;
  int b = int(f)%10;
  int c = round((f-int(f))*10);
  Serial.println(f,4);
  Serial.println(a,DEC);
  Serial.println(b,DEC);
  Serial.println(c,DEC);

  f = -24.8;

  a = int(f)/10;
  b = int(f)%10;
  c = round((f-int(f))*10);
  Serial.println(f,4);
  Serial.println(a,DEC);
  Serial.println(b,DEC);
  Serial.println(c,DEC);

  // array version

#define DIGITS 3
  int ar[DIGITS];

  f = -24.8;
  int t = round(f*10);
  int s = 1;
  if (t < 0) {
    t = -t; 
    s = -1; 
  }

  for (int i=DIGITS-1; i>=0; i--)
  {
    ar[i] = t %10;
    t /=10;
  }

  Serial.println(f,4);
  if (s==-1) Serial.println("-");
  for (int i=0; i<DIGITS; i++)
  {
    Serial.println(ar[i],DEC);
  }

  f = 24.8;
  t = round(f*10);
  s = 1;
  if (t < 0) {
    t = -t; 
    s = -1; 
  }

  for (int i=DIGITS-1; i>=0; i--)
  {
    ar[i] = t %10;
    t /=10;
  }

  Serial.println(f,4);
  if (s==-1) Serial.println("-");
  for (int i=0; i<DIGITS; i++)
  {
    Serial.println(ar[i],DEC);
  }

}

void loop(){}

Very nice too :-).... if this function is useful to lots of people I guess it will end up as a library one day.

My aim with all this is to read temperature from a TMP275 I2C sensor (haven't got that working yet - tried modifying the TMP102 library but I'm still missing something important) as a single decimal point value, say 12.3 and then writing this to 3 shift registers as a series of segment bytes controlling 3 seven segment digits.

I don't know if this is the best way of doing it but I thought that if I could seperate out the number into INTs I could then run each digit through some case statements to assign the right segment bytes before writing these to the shift registers.

Thanks for bringing this TMP275 I2C sensor to my attention. Very interesting data sheet.
(TMP275 pdf, TMP275 Description, TMP275 Datasheet, TMP275 view ::: ALLDATASHEET :::)

However, I understand the sensor itself does not provide float numbers ( and especially nothing with rounding issues like 24.8 ).

If you really need floats for some other cases, I'd suggest you don't round yourself, but work with the exact reading.
(no rounding issue with e.g. float t=24.75; )

If you don't need floats except for displaying more accuracy than full degrees, I'd simply interpret the hi byte as an integer temp, and translate the lower bits separately. The possible values depend on your chosen accuracy 9 .. 12 bit.
With 9 bit there will be only two cases: ".0" or ".5"
With full 12 bit accuracy, you mit pick the desired display digit from a char subdecimal[] = "0112233455678899"; with 16 entries for the least significant nibble (0x0 .. 0xF) as index.

Hi Michael,

Thanks for the info - that would certainly be one way to do it - I guess I was hoping to find/translate a library for the TMP275 which would have been spitting out a float value.... perhaps my next task should be to get the TMP275 working and go from there :slight_smile:

Why not just multiply by 10, handle as an integer, and then drop in a decimal point (and, if necessary, a leading zero)?