How do I round off a captured value?

Discussion in 'Embedded Systems and Microcontrollers' started by Neyolight, Apr 4, 2012.

  1. Neyolight

    Thread Starter Member

    Dec 13, 2011
    54
    3
    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 ?
     
  2. MrChips

    Moderator

    Oct 2, 2009
    12,452
    3,371
    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.
     
  3. Neyolight

    Thread Starter Member

    Dec 13, 2011
    54
    3
    Thanks Mr Chip. How does those 2 equations work ? :confused:

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

    Moderator

    Mar 31, 2012
    17,788
    4,808
    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: Apr 5, 2012
    Neyolight likes this.
  5. Markd77

    Senior Member

    Sep 7, 2009
    2,803
    594
    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.
     
    Neyolight likes this.
  6. MrChips

    Moderator

    Oct 2, 2009
    12,452
    3,371
    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
     
    Neyolight likes this.
  7. MrChips

    Moderator

    Oct 2, 2009
    12,452
    3,371
    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: Apr 5, 2012
    Neyolight likes this.
  8. WBahn

    Moderator

    Mar 31, 2012
    17,788
    4,808
    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.
     
  9. Neyolight

    Thread Starter Member

    Dec 13, 2011
    54
    3
    Thanks all. I tried to use "100 * int((N+50)/100)" . Here is my rounding code :
    Code ( (Unknown Language)):
    1.  
    2.  
    3.         unsigned volatile int temp_freq = 0;
    4.         unsigned volatile int check1 = 0;
    5.  
    6.         temp_freq = ReadTimer0(); //
    7.     check1 = 100*((temp_freq +50)/100);
    8.    
    9.  
    10.     ultoa(check1,str);
    11.    
    12.    
    13.    
    14.     putsUSART(str);
    15.  
    16.     putrsUSART("\n\r");
    17.  
    18.  
    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: Apr 9, 2012
  10. WBahn

    Moderator

    Mar 31, 2012
    17,788
    4,808
    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'?
     
    Neyolight likes this.
  11. Neyolight

    Thread Starter Member

    Dec 13, 2011
    54
    3
    I initialize str just below temp_freq and check 1 as -

    char str[30];
     
Loading...