C++ 将浮点数舍入到预定义点的规则网格

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11208971/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 14:56:33  来源:igfitidea点击:

Round a float to a regular grid of predefined points

c++rounding

提问by SteveL

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).

我无法更好地解释它,但这样做的原因是将点位置(如 0.131f、0.432f)转换为网格中瓷砖的位置(如 0.1f、0.4f)。

采纳答案by marton78

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
}

回答by Nim

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.

标准ceil(),floor()函数没有精度,我想可以通过添加自己的精度来解决这个问题 - 但这可能会引入错误 - 例如

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?

我想你可以测试看看这对你来说是否可靠?

回答by slashmais

An algorithm you can use:

您可以使用的算法:

  • get 10-to-the-power(number-of-significant-digits) (=P10)
  • multiply your double-value by P10
  • add: 0.5 (or subtract if negative - see Ankush Shah's comment)
  • divide the integer-portion of this sum by (P10) - the answer will be your rounded number
  • 得到 10 次方(有效位数)(=P10)
  • 将您的双值乘以 P10
  • 加:0.5(如果为负,则减去 - 请参阅 Ankush Shah 的评论)
  • 将此总和的整数部分除以 (P10) - 答案将是您的四舍五入数字

回答by eacousineau

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

编辑 1:我正在寻找 python 中的 numpy 解决方案,并没有意识到 OP 要求 C++ 哈哈,哦,好吧。

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.

编辑 2:大声笑,看起来我什至没有解决您最初的问题。看起来您真的想根据小数(操作与给定数字无关)而不是精度(操作取决于数字)进行四舍五入,其他人已经解决了这个问题。

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.

我实际上也在四处寻找这个,但找不到任何东西,所以我把一个 numpy 数组的实现放在一起。看起来它实现了slashmais所说的逻辑。

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):

这也是一个 C++ 标量版本,你可能可以使用 Eigen 做与上面类似的事情(它们有逻辑索引):(我也借此机会练习更多的提升哈哈):

#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

回答by Rafael Baptista

Use floor()and ceil(). floorwill convert a float to the next smaller integer, and ceilto the next higher:

使用floor()ceil()floor将浮点数转换为下一个较小的整数,并ceil转换为下一个更高的整数:

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

I think the following would work:

我认为以下方法可行:

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

floor( f + 0.5 )will round to an integer. By first multiplying by 10 and then dividing the result by 10 you are rounding by increments of 0.1.

floor( f + 0.5 )将舍入为整数。通过首先乘以 10,然后将结果除以 10,您将按 0.1 的增量四舍五入。

回答by ワイきんぐ

I will briefly optimize on the last few answers, converting the input number to a double first to prevent overflow. A sample function (not too pretty, but works just fine):

我将简要优化最后几个答案,首先将输入数字转换为双精度以防止溢出。一个示例函数(不太漂亮,但工作得很好):

#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;
}

回答by Victor

You can round a number to a desired precision with the following function

您可以使用以下函数将数字四舍五入到所需的精度

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

Check some examples below...

检查下面的一些例子......

1)

1)

round(5.252, 0)
returns => 5

2)

2)

round(5.252, 1)
returns => 5.3

3)

3)

round(5.252, 2)
returns => 5.25

4)

4)

round(5.252, 3)
returns => 5.252

This function works even for numbers with 9 precision.

此函数甚至适用于精度为 9 的数字。

5)

5)

round(5.1234500015, 9)
returns => 5.123450002

回答by Antonio

Usually you know the desired precision at compile time. Therefore, using the templated Pow function available here, you can do:

通常您在编译时就知道所需的精度。因此,使用此处提供的模板化 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;
}

Tested here.

在这里测试。

The result also shows how imprecise is floating point representation :)

结果还显示了浮点表示是多么不精确:)

3

3.099999905

3.140000105

3.14199996

3.141599894

3.141590118

3.141592979

3.141592741

3

3.099999905

3.140000105

3.14199996

3.141599894

3.141590118

3.141592979

3.141592741

You can have better results using double:

使用 double 可以获得更好的结果:

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

Printed with precision 20:

打印精度 20:

3

3.1000000000000000888

3.1400000000000001243

3.1419999999999999041

3.1415999999999999481

3.1415899999999998826

3.1415929999999998579

3.1415926999999999047

3

3.1000000000000000888

3.1400000000000001243

3.1419999999999999041

3.1415999999999999481

3.1415899999999998826

3.1415929999999998579

3.1415926999999999047

回答by KidNg

Algorithm for rounding float number:

四舍五入浮点数的算法:

 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;
    }

回答by SteveL

Since Mooing Duck edited my question and removed the code saying questions shouldn't contain answers (understandable), I will write the solution here:

由于 Mooing Duck 编辑了我的问题并删除了说问题不应包含答案的代码(可以理解),我将在此处编写解决方案:

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