Round a number to nearest

I want to round a number to the nearest 0.005 value.

Eg, the number is 0.243 so this rounds to 0.245 and 0.242 rounds to 0.240

You often see this on GPS systems where the distance only shows every 100m etc.

I’ve tried various approaches and can’t get it to round up exactly. This is driving a PWM output and I am seeing a candle like flickering due to the input signal being a little noisy at low brightness settings. I have an averaging filter but it still manages to leak to the output. I am trying to round this so that small input changes don’t cause the output PWM to toggle between 2 values.

I tried this but no good.


System.Math.Round(tempBrightness / 0.05F) * 0.05F;

It works sometimes but for example, 0.046 input comes out as 0.0449999981 which causes the brightness to change.

Not all floating numbers may be exactly represented with a finite number of bits.
While rounding, you may reduce the jitter, but not fully suppress it, because at some point a change of 1 LBS bit of the raw data will change the rounded value by 0.005.
You may multiply your data by 200 and cast to integer and perform the process using integer values, but it may be only a partial solution.
If your integer value changes, you may somewhat delay the change of the PWM value until the new value appears stable, avoiding the flickering due to bit noise.

@ SouthernStars.

I kind of guessed it was a floating point issue. The trouble is that the PWM driver expects a number between 0 and 1.0 and would have been better if this was 0-100 for percentage.

A pulse width input that toggles only 1 digit causes the output to change, causing the flickering effect. Something like 25 to 26 causes the reading to go from 0.059999998658895493 at 25 and 0.06 at 26. That is more than enough to cause flickering as it toggles between them.

No one is going to like the flickering effect. :slight_smile:

I may have to consider monitoring the readings over time and try to limit small changes. It’s only at low brightness that this happens.

You could try to increase your Input before rounding.
means
if you want to round to 0.05, just add up to 0.005 to you number.
This should work, if the consumer of you number simply cuts off the reamining Digits.

If your PWM (the output) is 12 bits, then convert the input value of your sensor as an integer NNew with the range [0…1023] and copy nNew to Nset

  1. send NSet/1024.0 to the PWM and clear a counter.

  2. While the input is again measured you convert the same way in NNew
    If the result equals the formerly sent value (i.e. NNew == NSet) clear the counter and goto 2) because there is no need to set the PWM.
    else
    if the counter is zero (first measure after setting the PWM),
    copy the new converterd value, NNew to NOld, don’t sent it to the PWM and increment the counter and goto 2).
    else
    if the sign of (NNew - NSet) is not the same as (NOld - NSet),
    clear the counter, and copy Nset to NOld, it was some noise, goto 2).
    else
    Increment the counter
    if the counter reaches some count (i.e. some time has elapsed) OR
    (abs(NNew - Nset) > some value greather than the expected noise)
    copy NNew to NSet GoTo 1) the change appears consistent
    else
    do nothing goto 2)

2 Likes

Mathematics

[url]c# - How do I round to the nearest 0.5? - Stack Overflow

How about this solution?

It is not about mathematics, it is a more general issue with any measure in any domain in the real world: there is noise.
In the presenrt case, it may translate to flickering if the input source is “constant” or changes with a slow rate.
The proposed solution avoids high frequency flickering, while, OTOH, allowing a fast response to a real change.

Like @ SouthernStars says this isn’t really a rounding or math issue, it is a system frequency response issue, i.e., balancing your system’s ability to respond to changes quickly without being overly sensitive to noise. Regardless of how you round the values going into or coming out of your averaging filter the output of the averaging filter can sit at some value that will cause the PWM to flicker even with a very small change on the input to the filter. Increasing the time constant of your averaging filter is about the only thing you can do to reduce the flicker but as SouthernStars mentioned your system frequency response will go down. If you don’t need to respond to changes quickly, that might not be a problem. Unless you have some external way to differentiate between real changes in the measured value and noise you’re kind of stuck. Ad hoc methods to look at the output and restrict changes over time are probably the same thing as increasing the filter time constant but with less predictable results. You may be able to improve the situation somewhat with a fancier filter. What kind of averaging filter are you using?

What’s the frequency of the PWM?

The input frequency is 2Khz and the output to the LED driver is 20Khz.

Just a simple addition and divide by the number of samples type. I am also considering using a moving average filter.

I’ll also give @ SouthernStars solution a go too.

Yes, a moving average filter is better. Use a circular buffer as well.

Ditto on the moving average filter. Here’s a moderately readable reference if you’re interested in the gory details.

http://www.analog.com/media/en/technical-documentation/dsp-book/dsp_book_Ch15.pdf

By the way, this is just one chapter from a pretty good reference that Analog Devices published many years ago and now is available free on line.


class Program
    {
        static void Main(string[] args)
        {
            MovingAverage average = new MovingAverage(10, 5);
            for(int i = 0; i< 10; i++)
            {
                Console.WriteLine(average.NextAverage(i));
            }
        }
    }

    public class MovingAverage
    {
        double[] buffer;
        unsafe uint lastValuePointer = 0;
        public MovingAverage(int bufferSize, double initialValue)
        {
            buffer = new double[bufferSize];
            for(int i = 0; i < bufferSize; i++)
            {
                buffer[i] = initialValue;
            }
        }

        public double NextAverage(double value)
        {
            int currentPointer = (int)lastValuePointer % buffer.Length;
            buffer[currentPointer] = value;
            double sum = 0;
            for (int i = 0; i < buffer.Length; i++)
            {
                sum += buffer[lastValuePointer % buffer.Length];
                lastValuePointer++;
            }
            lastValuePointer = (uint)currentPointer + 1;
            return sum / buffer.Length;
        }
    }

I tried my hand at making a moving average filter. Let me know if it works out.

After I removed the “unsafe” bit I was able to compile this and it works well. The larger the count the greater the smoothing effect. Nice work.

You should drop this into Codeshare as I am sure someone will find this handy to work with the likes of the noisy G120 ADC for example.

@ Dave McLaughlin - So problem solved?