Pages: [1]   Go Down
Author Topic: From float to seperate INTs - in an elegant manner :-)  (Read 1251 times)
0 Members and 1 Guest are viewing this topic.
UK
Offline Offline
Sr. Member
****
Karma: 1
Posts: 278
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Copenhagen, Denmark
Offline Offline
Edison Member
*
Karma: 32
Posts: 1211
Have you testrun your INO file today?
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

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

Offline Offline
Edison Member
*
Karma: 48
Posts: 1614
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The fraction needs to be rounded, otherwise the .8 will set c=7
Use this:
Code:
c=int((f-int(f)+0.05)*10);

Pete
Logged

Where are the Nick Gammons of yesteryear?

Copenhagen, Denmark
Offline Offline
Edison Member
*
Karma: 32
Posts: 1211
Have you testrun your INO file today?
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Damn, you're right ! smiley 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 smiley-wink and to keep it "elegant" use the round function for the last bit
Code:
c=round((f[n]-int(f[n]))*10) ;
Logged

Germany
Offline Offline
Faraday Member
**
Karma: 56
Posts: 3001
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

"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 )  smiley-wink
« Last Edit: May 05, 2012, 08:53:28 am by michael_x » Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 610
Posts: 49040
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
"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.
Logged

Germany
Offline Offline
Faraday Member
**
Karma: 56
Posts: 3001
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The %f format specifier does not work on the Arduino.
oops !  Thanks
Logged

UK
Offline Offline
Sr. Member
****
Karma: 1
Posts: 278
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 216
Posts: 13664
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


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 smiley-wink
Code:
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(){}





Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

UK
Offline Offline
Sr. Member
****
Karma: 1
Posts: 278
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Germany
Offline Offline
Faraday Member
**
Karma: 56
Posts: 3001
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks for bringing this TMP275 I2C sensor to my attention. Very interesting data sheet.
(http://pdf1.alldatasheet.com/datasheet-pdf/view/161348/BURR-BROWN/TMP275.html)

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.
Logged

UK
Offline Offline
Sr. Member
****
Karma: 1
Posts: 278
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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 :-)
Logged

Offline Offline
Full Member
***
Karma: 2
Posts: 197
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Pages: [1]   Go Up
Jump to: