Округлить поплавок до регулярной сетки заданных точек

Я хочу округлить число с плавающей точкой с заданной точностью, например:

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

Я не могу объяснить это лучше, но причина этого заключается в том, чтобы перевести точечное местоположение (например, 0,131f, 0,432f) в местоположение плитки в сетке (например, 0,1f, 0,4f).

Ответы

Ответ 1

Пока ваша сетка является регулярной, просто найдите преобразование из целых чисел в эту сетку. Итак, пусть ваша сетка

0.2  0.4  0.6  ...

Затем вы раунд

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

Ответ 2

Стандартные функции ceil(), floor() не имеют точности, я думаю, можно обойти это, добавив вашу собственную точность - но это может привести к ошибкам - например,

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

Думаю, вы могли бы проверить, действительно ли это для вас?

Ответ 3

Алгоритм, который вы можете использовать:

  • получить 10-к-силе (число значащих цифр) (= P10)
  • умножьте двойное значение на P10
  • add: 0.5 (или вычесть, если отрицательный - см. комментарий Ankush Shah)
  • разделите целочисленную часть этой суммы на (P10) - ответ будет вашим округленным числом

Ответ 4

EDIT 1: Я искал решения для numpy в python и не понимал, что OP попросил С++ haha, ну хорошо.

EDIT 2: Lol, похоже, я даже не обратился к вашему оригинальному вопросу. Похоже, вы действительно хотите округлить в соответствии с десятичной (операция не зависит от заданного числа), а не точность (операция зависит от числа), а другие уже обратились к этому.

Я тоже искал это, но не смог найти что-то, поэтому я собрал реализацию для массивов numpy. Похоже, что он реализует логику, о которой говорил слшмаи.

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

Здесь также скалярная версия С++, и вы, вероятно, могли бы сделать что-то подобное выше, используя Eigen (у них есть логическое индексирование): (Я также воспринял это как возможность попрактиковаться в еще одном повышении 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;
}

Вывод:

0.1
0
0.6
0.5

0.1
0
1
1

1.23
1.03e+06
0.00923

Ответ 5

Используйте floor() и ceil(). floor преобразует float в следующее меньшее целое число и ceil к следующему выше:

floor( 4.5 ); // returns 4.0
ceil( 4.5 );  // returns 5.0

Я думаю, что следующее будет работать:

float round( float f )
{   
    return floor((f * 10 ) + 0.5) / 10;
}

floor( f + 0.5 ) округляется до целого числа. Первым умножением на 10 и последующим делением результата на 10 вы округляете с шагом 0,1.

Ответ 6

Обычно вы знаете требуемую точность во время компиляции. Поэтому, используя функцию Templated Pow, доступную здесь, вы можете сделать:

template <int PRECISION>
float roundP(float f)
{
    const int temp = Pow<10,PRECISION>::result;
    return roundf(f*temp)/temp;
}

int main () {
    std::cout << std::setprecision(10);
    std::cout << roundP<0>(M_PI) << std::endl;
    std::cout << roundP<1>(M_PI) << std::endl;
    std::cout << roundP<2>(M_PI) << std::endl;
    std::cout << roundP<3>(M_PI) << std::endl;
    std::cout << roundP<4>(M_PI) << std::endl;
    std::cout << roundP<5>(M_PI) << std::endl;
    std::cout << roundP<6>(M_PI) << std::endl;
    std::cout << roundP<7>(M_PI) << std::endl;
}

Протестировано здесь.

Результат также показывает, насколько неточным является представление с плавающей запятой:)

3

3,099999905

3,140000105

3.14199996

3,141599894

3,141590118

3,141592979

3,141592741

У вас могут быть лучшие результаты с использованием double:

template <int PRECISION>
double roundP(double f)
{
    const int temp = Pow<10,PRECISION>::result;
    return round(f*temp)/temp;
}

Отпечатано с точностью 20:

3

+3,1000000000000000888

+3,1400000000000001243

+3,1419999999999999041

+3,1415999999999999481

+3,1415899999999998826

+3,1415929999999998579

+3,1415926999999999047

Ответ 7

Я кратко оптимизирую последние несколько ответов, сначала преобразуя число ввода в двойное, чтобы предотвратить переполнение. Примерная функция (не слишком красивая, но отлично работает):

#include <cmath>

// round float to n decimals precision
float round_n (float num, int dec)
{
    double m = (num < 0.0) ? -1.0 : 1.0;   // check if input is negative
    double pwr = pow(10, dec);
    return float(floor((double)num * m * pwr + 0.5) / pwr) * m;
}

Ответ 8

Так как Mooing Duck отредактировал мой вопрос и удалил код, говорящий, что вопросы не должны содержать ответы (понятно), я напишу решение здесь:

float round(float f,float prec)
{
    return (float) (floor(f*(1.0f/prec) + 0.5)/(1.0f/prec));
}

Ответ 9

Алгоритм для округления числа с плавающей запятой:

 double Rounding(double src, int precision) {
         int_64 des;
         double tmp;
         double result;
         tmp = src * pow(10, precision);
         if(tmp < 0) {//negative double
            des = (int_64)(tmp - 0.5);
         }else {
            des = (int_64)(tmp + 0.5);
         }
         result = (double)((double)dst * pow(10, -precision));
         return result;
    }

Ответ 10

Вы можете округлить число с желаемой точностью с помощью следующей функции

double round(long double number, int precision) {
  int decimals = std::pow(10, precision);
  return (std::round(number * decimals)) / decimals;
}

Проверьте некоторые примеры ниже...

1)

round(5.252, 0)
returns => 5

2)

round(5.252, 1)
returns => 5.3

3)

round(5.252, 2)
returns => 5.25

4)

round(5.252, 3)
returns => 5.252

Эта функция работает даже для чисел с точностью до 9.

5)

round(5.1234500015, 9)
returns => 5.123450002