Go Down

Topic: Round Function (Read 183740 times) previous topic - next topic

sspence65

I like to develop my formulae in a spreadsheet, before moving it into code. I often use round() in my spreadsheet work, and copied a formula using that to Arduino, not realizing that round() does not exist in the Arduino reference.

I've seen some convoluted ways to make numbers round up or down, but find the generic C/C++ Round function, round(), works just fine:

a = round(b);

If b = 12.5, then a = 12, but if b = 12.6, then a = 13.

http://www.codecogs.com/reference/computing/c/math.h/round.php

How many other standard c math functions are undocumented in Arduino, but still work?
Steve Spence - KK4HFJ

dc42


How many other standard c math functions are undocumented in Arduino, but still work?


Most of the standard C library is available. The main exceptions are the I/O functions that don't make sense without a disk filing system or standard input/output/error streams. You can find the header files in C:\arduino-1.0\hardware\tools\avr\avr\include on a standard Windows installation.
Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

majenko

round() exists.  It may not be documented by Arduino, but then it's not an Arduino function.

It is one of the standard C functions, so it is available, as the Arduino compiler is just a standard (avr-gcc) C++ compiler.

gardner

Arduino relies on the AVR-libc for the standard libraries.  The reference for <math.h> is here: http://www.nongnu.org/avr-libc/user-manual/group__avr__math.html
Sketches will be linked against this library, if needed, and you can use any of these math function.  There is a round(), by the way, but it only rounds to the nearest integer, not to a set number of decimal places.

GoForSmoke

LOL

Code: [Select]

float foo = 12.5;
int bar = (int) foo; // bar now = 12
foo = (float) bar;   // foo now = 12 or something very close but that's how FP is and always has been.


want to round off a bit deeper?

Code: [Select]

float foo = 12.34567;
int bar = (int) foo * 1000.0; // bar now = 12345
foo = (float) bar / 1000.0;   // foo now = 12.345 or something very close but that's how FP is and always has been.


For my first 2 decades of computing, FP also = slow (as well as quirky) and with these chips it still is! The answer is to learn to use fixed point, ala bar = 12345 to store 12.345 which is only a pain when printing, and a minor one at that.

Code: [Select]

int bar = 12345;
Serial.print( bar / 1000 );
Serial.print( "." );
bar = bar % 1000;
int decis = 100;
while ( decis > 0 )
{
 if ( bar < decis )
 {
   Serial.print( "0" );  
   decis /= 10;
 }
 else  decis = 0;
}
Serial.print( bar );


I could comment these but if you need help understanding what they do then don't bother messing with them.
1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

majenko

Your "rounding" using casting to int and then back to float isn't rounding.

That is the equivalent to the "floor()" function.

A round function should take anything below x.5 and round it to x.  Anything x.5 and upwards should round to x+1.

So 10.499999999 would round to 10, but 10.5 would round to 11.

For example, this is how the BSD round() function is written:

Code: [Select]

double
round(double x)
{
        double t;

        if (!isfinite(x))
                return (x);

        if (x >= 0.0) {
                t = floor(x);
                if (t - x <= -0.5)
                        t += 1.0;
                return (t);
        } else {
                t = floor(-x);
                if (t + x <= -0.5)
                        t += 1.0;
                return (-t);
        }
}

GoForSmoke

So add .5 to the FP before casting to int... lah-dee-dee, lah-dee-dah.

Arduino float and double are both 32-bit IEEE inaccurate FP.


1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

majenko

Quote
So add .5 to the FP before casting to int.


That's fine if the value is >0, but what if its a negative value?  you'll want to subtract 0.5 from the value in that case.

sspence65

That's where round() works no nicely and neatly. No finagling to get things to drop into place. It really should be mentioned in the Arduino reference under math, as it's a commonly needed function, and I see so many "workarounds" for a problem that does not exist. I had a well known Arduino library author ask me what round was in my code, he hadn't seen it before ......

Steve Spence - KK4HFJ

GoForSmoke

Actually what I want to do is avoid using FP whenever possible. And the fact is that I did a lot of avoiding unnecessary FP for the 19 years I wrote code for money. Sometimes it was the way to go and yes I did have different functions just to get it to display the 'real number' (ex: 1.0 instead of .99999994289534) because that's how IEEE FP is.

Want to be fast and accurate? Use longs with fixed point. Want to be bigger? Make a class that uses a long for the digits and a signed byte for the power of 10 that covers all the functions you will need, and that could take a few K of flash right there.

For the most part though, longs can cover needs all by themselves. 9 digits is plenty most of the time, you just gotta find the scale to fit your needs. It is a question like do you count dollars or pennies?

Yeah I can grab libraries and get those "neat little functions" that again and again aren't so neat or little except for the amount of typing I must do. And every use comes at FP cost in cycles. Fat.. slow.. (quirky number) code. Just what I don't want!
1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

majenko

Q15 and Q16 is much better (what I use for DSP work), and yes, float (and double) should be avoided whenever possible unless you have a hardware FPU.

VopA

I have helped myself with this code:

Code: [Select]

float truncate(float val, byte dec)
{
    float x = val * pow(10, dec);
    float y = round(x);
    float z = x - y;
    if ((int)z == 5)
    {
        y++;
    } else {}
    x = y / pow(10, dec);
    return x;
}


dec value represents how many decimals you want to shrink your value to.

Go Up