Arduino Forum

Using Arduino => Programming Questions => Topic started by: _mjw_ on Dec 27, 2013, 06:29 am

Title: Unexpected output when converting to an integer with int()
Post by: _mjw_ on Dec 27, 2013, 06:29 am
Hi,
Just beginning with arduino and am writing code to display a binary counter with diodes.  I realize that one could simply use bitRead(count,b) to extract the bth bit of the counter count, but on the way, I have tried
count & int(pow(2,b)).   Converting 2b to integer is yielding some unexpected results. 
The output was sent to the Serial Monitor. 
Here is the code:
Code: [Select]

void setup()
{
  int index;
  Serial.begin(9600);
  for (int k=0; k<=7; k++) {
    float m = pow(2,k);
    int q = int(m);
    Serial.print("m = ");
    Serial.print(m);
    Serial.print("\t q = ");
    Serial.println(q);
  }
}
void loop()
{
}


This is the output:

m = 1.00    q = 1
m = 2.00    q = 2
m = 4.00    q = 3
m = 8.00    q = 7
m = 16.00   q = 15
m = 32.00   q = 31
m = 64.00   q = 63
m = 128.00  q = 127


Any ideas on why this is happening?  Perhaps int() is rounding down?
Thank you!
Title: Re: Unexpected output when converting to an integer with int()
Post by: _mjw_ on Dec 27, 2013, 09:03 am
Ah, p. 77 in the Arduino Cookbook states that when casting  to an integer, floating-point numbers do not round correctly.  It seems that 2^4 comes up with something a little less than 16.00 and when cast as an integer,  int(16.00 - a_tiny_bit) = 15.
Very unexpected!
Title: Re: Unexpected output when converting to an integer with int()
Post by: PaulS on Dec 27, 2013, 03:05 pm
Quote
Ah, p. 77 in the Arduino Cookbook states that when casting  to an integer, floating-point numbers do not round correctly.

Floats are NOT rounded when converted to ints. They are truncated.

Quote
Very unexpected!

Not by me.
Title: Re: Unexpected output when converting to an integer with int()
Post by: holmes4 on Dec 27, 2013, 03:15 pm
ALL integer maths "rounds" down. Try 3/2

Mark
Title: Re: Unexpected output when converting to an integer with int()
Post by: _mjw_ on Dec 27, 2013, 05:18 pm

Floats are NOT rounded when converted to ints. They are truncated.

Quote
Quote
Very unexpected!

Not by me.

Okay.  If the expected behavior is to truncate, then yes, it is working.  I was (and am) still surprised to find
that 2^4 < 16.0.  (Probably better would be to use bitshift left  (1<<4) and keep everything always integer).



ALL integer maths "rounds" down. Try 3/2

And   -3/2  results in  -2.  Seems that if we want to round rather than truncate, we should add 1.0/2.0
before casting as integer, yes?


Thank you both for the helpful comments!  It has been years since programming in C and it will be nice to become familiar again with details like these ...
Title: Re: Unexpected output when converting to an integer with int()
Post by: mrburnette on Dec 27, 2013, 05:53 pm
Quote
It has been years since programming in C and it will be nice to become familiar again with details like these ...


... and if you programmed on a PC or minicomputer in C, then C++ for microcontrollers is even more distant.  
Underneath the Arduino GUI is the AVR GCC compiler:
http://www.avrfreaks.net/wiki/index.php/Documentation:AVR_GCC#Getting_started_with_AVR-GCC (http://www.avrfreaks.net/wiki/index.php/Documentation:AVR_GCC#Getting_started_with_AVR-GCC)

There is also the AVR-LIBC library that is available:
http://www.nongnu.org/avr-libc/ (http://www.nongnu.org/avr-libc/)
and use with Arduino introduced here:
http://arduino.cc/en/Reference/UsingAVR (http://arduino.cc/en/Reference/UsingAVR)

Things to keep in mind:


The compiler does a tremendous amount of optimizations ... sometimes taking out some of your code or even rearranging it.  Never assume.  If you really want to know what happened, you can get the assembler source with just a little work.

IF you are using Windows, you can also get information on the Flash and SRAM usage of your code with a little scripting:
(adjust paths are required)
Code: [Select]

PATH=%path%;C:\Program Files\Arduino_105\hardware\tools\avr\utils\bin;
CD %TEMP%
MD %PUBLIC%\ELFtemp
for /R %TEMP% %%f in (*.elf) do XCOPY /D /Y %%f %PUBLIC%\ELFtemp\
DIR %PUBLIC%\ELFtemp\*.elf /s /b /O:-D /T:W >ElfRhere
SET /P ELF= <ElfRhere
ECHO %ELF% >MemUsage.txt
AVR-SIZE -C %ELF% >>MemUsage.txt
NOTEPAD MemUsage.txt
SET ELF=""


Ray
Title: Re: Unexpected output when converting to an integer with int()
Post by: _mjw_ on Dec 27, 2013, 06:49 pm

... and if you programmed on a PC or minicomputer in C, then C++ for microcontrollers is even more distant.  

Actually, since then a little bit with Linux, but mostly on a SPARC (Unix) !

Quote

Underneath the Arduino GUI is the AVR GCC compiler:
http://www.avrfreaks.net/wiki/index.php/Documentation:AVR_GCC#Getting_started_with_AVR-GCC (http://www.avrfreaks.net/wiki/index.php/Documentation:AVR_GCC#Getting_started_with_AVR-GCC)

There is also the AVR-LIBC library that is available:
http://www.nongnu.org/avr-libc/ (http://www.nongnu.org/avr-libc/)
and use with Arduino introduced here:
http://arduino.cc/en/Reference/UsingAVR (http://arduino.cc/en/Reference/UsingAVR)

Things to keep in mind:

  • You can generally swing between Arduino'ish commands and C++ commands

  • You can resort to extern C {} or you can do inline assembler as advance coding.



Thank you very much!  Should keep me busy for a while!

Quote

The compiler does a tremendous amount of optimizations ... sometimes taking out some of your code or even rearranging it.  Never assume.  If you really want to know what happened, you can get the assembler source with just a little work.

Very nice!  Hope to learn about this too!

Quote

IF you are using Windows, you can also get information on the Flash and SRAM usage of your code with a little scripting:

May be a reason to check out windows ...,  wonder if it can also be done on a Mac?  A very quick online search
seems to show that it can (again, Arduino Cookbook, Chap. 17).

Thank you for all the suggestions.  Looking forward to reading about all of this!
Title: Re: Unexpected output when converting to an integer with int()
Post by: mrburnette on Dec 28, 2013, 01:08 am
Quote
wonder if it can also be done on a Mac?  A very quick online search
seems to show that it can (again, Arduino Cookbook, Chap. 17).


By polypagan for Linux:
Quote
Not tested, but my bash script should also work just fine on MacOS (in terminal, of course).

http://forum.arduino.cc/index.php?topic=196101.msg1450173#msg1450173 (http://forum.arduino.cc/index.php?topic=196101.msg1450173#msg1450173)
Title: Re: Unexpected output when converting to an integer with int()
Post by: michinyon on Dec 28, 2013, 05:19 am

Ah, p. 77 in the Arduino Cookbook states that when casting  to an integer, floating-point numbers do not round correctly.  It seems that 2^4 comes up with something a little less than 16.00 and when cast as an integer,  int(16.00 - a_tiny_bit) = 15.
Very unexpected!


That's not unexpected at all.  All computers work that way.   
Title: Re: Unexpected output when converting to an integer with int()
Post by: _mjw_ on Dec 30, 2013, 09:30 pm
Quote

That's not unexpected at all.  All computers work that way.   

Not all.
The following C code (OS X 10.8.5, gcc) produces expected output:
Code: [Select]

#include <stdio.h>
#include <math.h>

main()
{
  int index, k, q;
  float m;
  for (k=0; k<=7; k++) {
    m = pow(2,k);
    q = (int) m;
    printf("m = %f",m);
    printf("\t q = %d\n",q);
  }
}

Here is the resulting output:

m = 1.000000    q = 1
m = 2.000000    q = 2
m = 4.000000    q = 4
m = 8.000000    q = 8
m = 16.000000   q = 16
m = 32.000000   q = 32
m = 64.000000   q = 64
m = 128.000000  q = 128

Could someone please explain why the Arduino produces different results?
Thank you!
Title: Re: Unexpected output when converting to an integer with int()
Post by: AWOL on Dec 30, 2013, 09:36 pm
32 bit float vs. 64 bit float
Title: Re: Unexpected output when converting to an integer with int()
Post by: _mjw_ on Dec 30, 2013, 09:49 pm

32 bit float vs. 64 bit float

Recompiled with the option -m32 (to enforce 32 bit calculations) and got the same results!
Title: Re: Unexpected output when converting to an integer with int()
Post by: mrburnette on Dec 31, 2013, 12:46 am
Quote
Could someone please explain why the Arduino produces different results?
Thank you


Isn't the real question, what happens with Atmel Studio on a naked 328P with all the latest packages?

The issue may just be with the AVR-GCC math library.  Arduino does not package the latest version of AVR-GCC, so we could be looking at an already "fixed" issue.

Things like this can get under ones skin... When in college years ago, I was dismayed to find my TI scientific calculator would not answer 0 to sin(45) - cos(45) while the dude that sat across from me had an HP and it did give 0.  Needless to say, my anal self sold the TI and bought an HP67... And never looked back!

Ray
Title: Re: Unexpected output when converting to an integer with int()
Post by: SirNickity on Dec 31, 2013, 01:08 am
In general, it's best to avoid floats anywhere you can use integers.  For one, go browse the avr-libc handbook, particularly the math stuff.  You'll find a lot of it uses floats as parameters by default.  Since there's no floating-point hardware in an AVR mega or tiny, that all has to be done via software emulation, which is extra code bloat that you probably didn't need for your intended application.

For that reason, be aware of the math functions you use and try to implement them in more efficient ways if you can.  E.g., definitely use shifts to do powers of 2.  You'll get a more accurate result, it'll execute faster (a relative term), and assuming you don't use floats anywhere else, it'll save all the flash space required to include the fp math library.

Naturally, when you need non-integer values and can't get by with fixed-point math, well ya godda do whatcha godda do.  And none of this applies in test cases and matters of curiosity like in this thread.
Title: Re: Unexpected output when converting to an integer with int()
Post by: _mjw_ on Dec 31, 2013, 01:28 am
Quote

Isn't the real question, what happens with Atmel Studio on a naked 328P with all the latest packages?

Not sure.  Haven't tried Atmel Studio.  Is that an application only for windows?
Quote

The issue may just be with the AVR-GCC math library.  Arduino does not package the latest version of AVR-GCC, so we could be looking at an already "fixed" issue.

Thank you for suggesting it may be something with the AVR-GCC math library.  Don't think I'll have time to compare the latest version to whatever is implemented in the Arduino software (am simply using the Arduino project Version 1.0.5) .

Looks like I was not the first one surprised by this!  Check out this link:
http://8515.avrfreaks.net/index.php?name=PNphpBB2&file=printview&t=80319&start=0
Quote

Things like this can get under ones skin [...] bought an HP67... And never looked back!

The HPs are nice calculators!
Title: Re: Unexpected output when converting to an integer with int()
Post by: _mjw_ on Dec 31, 2013, 01:39 am

In general, it's best to avoid floats anywhere you can use integers.  For one, go browse the avr-libc handbook, particularly the math stuff.  You'll find a lot of it uses floats as parameters by default.  Since there's no floating-point hardware in an AVR mega or tiny, that all has to be done via software emulation, which is extra code bloat that you probably didn't need for your intended application.

Very interesting.  Will have to have a look at the avr-libc handbook.
Quote

For that reason, be aware of the math functions you use and try to implement them in more efficient ways if you can.  E.g., definitely use shifts to do powers of 2.  You'll get a more accurate result, it'll execute faster (a relative term), and assuming you don't use floats anywhere else, it'll save all the flash space required to include the fp math library.

Agreed.  Originally performed this calculation to get at each of the bits in a counter.  Using bitshift left (<<) or bitread() is more direct.
Quote

Naturally, when you need non-integer values and can't get by with fixed-point math, well ya godda do whatcha godda do.  And none of this applies in test cases and matters of curiosity like in this thread.

Yes, it is a matter of curiosity, but interesting enough to others also, I hope.
Title: Re: Unexpected output when converting to an integer with int()
Post by: Coding Badly on Dec 31, 2013, 02:31 am
The following C code (OS X 10.8.5, gcc) produces expected output:
Could someone please explain why the Arduino produces different results?


Rounding.  These are the rules of interest...
http://en.wikipedia.org/wiki/IEEE_floating_point#Rounding_rules
In addition, Serial.print may use a rounding rule not on that list.

Which rule is used by your PC program?  Which rule is used by AVR Libc?  Which rule is used by the Arduino Run Time Library?  Is it possible your PC program is using a different rule than your Arduino program?


Or, intermediates...

Quote
Quote
32 bit float vs. 64 bit float

Recompiled with the option -m32 (to enforce 32 bit calculations) and got the same results!


Incorrect.  A modern 32 bit processor (like a Pentium) simply does not support operations on 32 bit floating point numbers.  They are capable of storing and loading 32 bit floats but all operations are done at 64 bits (or higher) to avoid problems with intermediate values.
Title: Re: Unexpected output when converting to an integer with int()
Post by: MarkT on Dec 31, 2013, 02:48 am

Quote

That's not unexpected at all.  All computers work that way.   

Not all.
The following C code (OS X 10.8.5, gcc) produces expected output:
Code: [Select]

#include <stdio.h>
#include <math.h>

main()
{
  int index, k, q;
  float m;
  for (k=0; k<=7; k++) {
    m = pow(2,k);
    q = (int) m;
    printf("m = %f",m);
    printf("\t q = %d\n",q);
  }
}

Here is the resulting output:

m = 1.000000    q = 1
m = 2.000000    q = 2
m = 4.000000    q = 4
m = 8.000000    q = 8
m = 16.000000   q = 16
m = 32.000000   q = 32
m = 64.000000   q = 64
m = 128.000000  q = 128

Could someone please explain why the Arduino produces different results?
Thank you!


Because floating point results are inexact, you cannot make assumptions like this.
Different implementations of pow, for instance, may special-case when the arguments
happen to be integral (on the Arduino there is no room for special cases, the standard
method using logarithms will be used with the expected imprecision in the LSBs)

To get integer powers of two you use the << operator.  1 << n
Title: Re: Unexpected output when converting to an integer with int()
Post by: SirNickity on Dec 31, 2013, 03:04 am
Because floating point results are inexact, you cannot make assumptions like this.


This is a profound point.  To any programmers that aren't already aware of the significance of this statement, it's something you'll want to read up on.

The gist is this:  Floating point values don't store exact values, they store approximations.  There are many integer values that can't be stored as floating point values, but will instead be just slightly off.  The implications of this are obvious for cumulative math -- and particular, code that deals with money.  Higher precision does not solve the root problem.  You may have 64 bits of precision, but all that means is you might get a whole bunch of .999999s when what you really wanted is .0.

(WOOHOO!  1000 posts.  And I am no longer a God member.  Edison outranks God?  Well, I guess they both said "Let there be light."  ;))
Title: Re: Unexpected output when converting to an integer with int()
Post by: _mjw_ on Dec 31, 2013, 11:47 pm

Because floating point results are inexact, you cannot make assumptions like this.
Different implementations of pow, for instance, may special-case when the arguments
happen to be integral (on the Arduino there is no room for special cases, the standard
method using logarithms will be used with the expected imprecision in the LSBs)
To get integer powers of two you use the << operator.  1 << n

Agreed.  Was also stated above.

Because floating point results are inexact, you cannot make assumptions like this.

This is a profound point.  To any programmers that aren't already aware of the significance of this statement, it's something you'll want to read up on.

Yes.  The arduino chip is using logarithms to calculate powers, and not many bits at that.  Those beyond the sixth after the decimal in the final result are often off.