Round a float to a regular grid of predefined points

I want to round a float number to a given precision, for example :

0.051 i want to convert it to
0.1

0.049 i want to convert it to
0.0

0.56 i want to convert it to
0.6

0.54 i want to convert it to
0.5

I cant explain it better, but the reason for this is to translate a point location (like 0.131f, 0.432f) to the location of tile in a grid (like 0.1f, 0.4f).


Solution 1:

As long as your grid is regular, just find a transformation from integers to this grid. So let's say your grid is

0.2  0.4  0.6  ...

Then you round by

float round(float f)
{
    return floor(f * 5 + 0.5) / 5;
    // return std::round(f * 5) / 5; // C++11
}

Solution 2:

The standard ceil(), floor() functions don't have a precision, I guess could work around that by adding your own precision - but this may introduce errors - e.g.

double ceil(double v, int p)
{
  v *= pow(10, p);
  v = ceil(v);
  v /= pow(10, p);
}

I guess you could test to see if this is reliable for you?

Solution 3:

EDIT 1: I was looking for solutions for numpy in python and didn't realize that the OP asked for C++ haha, oh well.

EDIT 2: Lol, looks like I didn't even address your original question. It looks like you're really wanting to round off according to a decimal (operation is independent of the given number), not a precision (operation is dependent on the number), and the others have already addressed that.

I was actually looking around for this too but could not find something, so I threw together an implementation for numpy arrays. It looks like it implements the logic that slashmais stated.

def pround(x, precision = 5):
    temp = array(x)
    ignore = (temp == 0.)
    use = logical_not(ignore)
    ex = floor(log10(abs(temp[use]))) - precision + 1
    div = 10**ex
    temp[use] = floor(temp[use] / div + 0.5) * div
    return temp

Here's a C++ scalar version as well, and you could probably do something similar to above using Eigen (they have logical indexing): (I also took this as a chance to practice some more boost haha):

#include <cmath>
#include <iostream>
#include <vector>
#include <boost/foreach.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>

using namespace std;

double pround(double x, int precision)
{
    if (x == 0.)
        return x;
    int ex = floor(log10(abs(x))) - precision + 1;
    double div = pow(10, ex);
    return floor(x / div + 0.5) * div;
}

    template<typename T>
vector<T>& operator<<(vector<T> &x, const T &item)
{
    x.push_back(item);
    return x;
}

int main()
{
    vector<double> list;
    list << 0.051 << 0.049 << 0.56 << 0.54;
    // What the OP was wanting
    BOOST_FOREACH(double x, list)
    {
        cout << floor(x * 10 + 0.5) / 10 << "\n";
    }

    cout << "\n";

    BOOST_FOREACH(double x, list)
    {
        cout << pround(x, 0) << "\n";
    }

    cout << "\n";

    boost::function<double(double)> rounder = boost::bind(&pround, _1, 3);
    vector<double> newList;
    newList << 1.2345 << 1034324.23 << 0.0092320985;
    BOOST_FOREACH(double x, newList)
    {
        cout << rounder(x) << "\n";
    }

    return 0;
}

Output:

0.1
0
0.6
0.5

0.1
0
1
1

1.23
1.03e+06
0.00923