How do I round off a captured value?

Thread Starter

Neyolight

Joined Dec 13, 2011
54
Hi all

I am using PIC18F4620 to measure frequency. My frequency reading vary a lot -randomly , so I thought of rounding these values to nearest 100. How can I possibly do that in code ? Thanks

Im using C18 for PIC18F4620. Is there any inbuilt "rounding function" ?

Frequency Output:
13341
13379
13419
13326
13368
13408
13448
13357
13392
13427


Want to round it to nearest 100 :

13300
13300
13400...



Cheers

ps : Is it possible to round it to nearest 50 ?
 

MrChips

Joined Oct 2, 2009
30,714
To round N to the nearest 100, assuming N is always positive

100 * int((N+50)/100)

To round N to the nearest 50,

50 * int((N+25)/50)

But if your data is fluctuating you would be better off to take an average value instead of rounding.
 

Thread Starter

Neyolight

Joined Dec 13, 2011
54
To round N to the nearest 100, assuming N is always positive

100 * int((N+50)/100)

To round N to the nearest 50,

50 * int((N+25)/50)

But if your data is fluctuating you would be better off to take an average value instead of rounding.
Thanks Mr Chip. How does those 2 equations work ? :confused:

I will try those two equations right now and see my result ! :)
 

WBahn

Joined Mar 31, 2012
29,979
Instead of explaining how they work, let's take the reverse and see how they can be arrived at from scratch.

First, let's consider how you would round a value to the nearest whole number. Let's say you have two numbers, 3.6 and 3.4. You want to round the first up to 4 and the second number down to 3. Now, consider that when you truncate the fractional part off a number, you simply remove it, no matter what it is. Another way of thinking of this is that you round down (we are talking about positive numbers here, the rules for negative numbers are ambiguous). That means that 3.01 and 3.99 would both be truncated to 3. The int() function performs the truncation (and there are other ways to do it, in fact just performing division between two integers should automatically result in a truncated integer result).

So, getting back to our example, we want to do something to our number such that any value whose fractional part is 0.5 or larger will end up having an whole part greater than it's present whole part (i.e., so that 3.6 becomes 4.something) while doing the same thing to a fractional value below 0.5 doesed change the whole part (i.e., so that 3.4 becomes 3.something). How about if we add 0.5 to the number? Then 3.6 becomes 4.1 while 3.4 becomes 3.9. Now we just have to truncate the fractional part and you have the desired result.

x = int(x+0.5)

Now, let's consider a related problem in which you want to truncate the value so that it is a multiple of, say, 100. Let's use 3140. If we divide by 100, we have 31.40. We now just want to get rid of the fractional part, which we can do with the int() function, leaving us with 31. To get our final result, we simply re-multiply by the 100. To trancate so that the result is an even multiple of V, then we simply go:

x = V * int(x/V)

We can combine these two steps in a couple of different ways. We can add half of V first, or just let the 0.5 do it for us. Do NOT do both. So either:

x = V * int((x+0.5*V)/V

or

x = V * int(x/V + 0.5)

Depending on how good your compiler is at optimization when targeting an integer-only platform (or even one that does not support multiplication/division at all), you may or may not get the same ultimate code. If you don't, the performance difference can be very significant.

Because multiplication and, especially, division are so expensive on most cheap embedded processors, you are often better off to be a bit more brute force in your code in order to avoid it.
 
Last edited:

Markd77

Joined Sep 7, 2009
2,806
Presumably you are converting the number to BCD to display it. That might be the best place to do the rounding, it would certainly remove the uncertainty of how much code the compiler will generate for the multiply and divide. To round to the nearest 100 you would just need to check if the tens digit is 5 or more, if so add 1 to the hundreds digit to display.
 

MrChips

Joined Oct 2, 2009
30,714
On a MCU rounding using multiplication and division in ASM or C is not a problem.
Suppose you wish to round to the nearest 16.

N = 16*int ((N+8)/16)

Replace multiplication with left shift and division with right shift

N = ((N+8) >> 4) << 4
 

MrChips

Joined Oct 2, 2009
30,714
I would still perform a running average instead of rounding.

Suppose you wish to average N = 8 data points, x= new data
You will need two registers or variables:

avg
sum

sum = sum + x - avg
avg = sum/N

if N = 8, replace the division with right shift

avg = sum >> 3

Make sure that the size of the register sum can hold N times avg. For example, if N = 8 and sum = 16 bits, then x must not exceed 13 bits, i.e. 8191.
 
Last edited:

WBahn

Joined Mar 31, 2012
29,979
You can replace multiplication and division by shifts only if the multiplicand and/or divisor is an integral power of two. It doesn't help too much in the general case (rounding to the nearest 100, for example).

However, if you have enough data, you can play some games and get both averaging and rounding at the same time, provided you have the freedom to set and live with some constraints.
 

Thread Starter

Neyolight

Joined Dec 13, 2011
54
Thanks all. I tried to use "100 * int((N+50)/100)" . Here is my rounding code :
Rich (BB code):
        unsigned volatile int temp_freq = 0;
        unsigned volatile int check1 = 0;

        temp_freq = ReadTimer0(); // 
	check1 = 100*((temp_freq +50)/100);
	

	ultoa(check1,str);
	
	
	
	putsUSART(str);

	putrsUSART("\n\r");
I have a small problem, in the hyperterminal my output seems correctly rounded to nearest 100 but when I save those data it changes to random characters.

Output on hyperterminal:
6700
6700
6700
6700
6600
6700
6700
6700
6700
6700
6700
6700
6700
6700
6700
6700
6700
6700


When saved in a text file :
㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〶0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〶0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〶0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〶0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〶0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〶0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ㘀〷0ഊ


Why is this happening?
 
Last edited:

WBahn

Joined Mar 31, 2012
29,979
Without knowing more details, it would appear that this is either being being saved as a binary file or, when you read it back, read as a binary file. Check all of your Hyperterminal settings, particularly the receive settings, to make sure that data is being saved consistent with out it was received.

While this shouldn't have an impact given the symptoms you are describing, where and how are you declaring your variable 'str'?
 
Top