【问题标题】:Round a float to a regular grid of predefined points将浮点数舍入为预定义点的规则网格
【发布时间】:2012-06-26 13:57:07
【问题描述】:

我想将浮点数四舍五入到给定的精度,例如:

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

【问题讨论】:

  • 将浮点数四舍五入到小数点后一位或多位没有多大意义;像0.1 这样的数字不能用二进制浮点数精确表示。可以对输出进行舍入(到字符串或文件)。
  • 我们在 IT 部门,我们试图用代码行来表示现实世界的无限可能性,在这里一切都有意义。我将它用于游戏中的无限滚动背景,失去精度并不重要。
  • 我已经对此进行了测试,但它似乎无法正常工作。如果您尝试舍入 127,它会返回 128(我使用 double 而不是 float 来表示 f 和返回类型)。
  • @BlunT ,可能你没有得到函数的意义,你传递的精度是多少?如果您输入 2,您将得到 128,因为您将从 0,2,4,6...,128,130...(2 作为增量)中获得最接近的数字,如果您通过 127.456 和精度 0.01f,您将获得 127.46因为 127.46 最接近 0,0.01,0.02f.....127.45,127.46,127.47(0.01 作为增量)
  • @SteveL 那么在这种情况下你是对的,因为函数向上舍入到最接近的对数,我使用 2 作为精度。尽管如此,我认为问题的标题可能具有误导性。在我看来,考虑到其他编程语言中的round的其他实现,精度是指小数位数。

标签: c++ rounding


【解决方案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
}

【讨论】:

  • 似乎无法正常工作。 0.5 转换为 0.6。
  • @Niki ... 这正是您所期望的,不是吗?
  • 不完全是 Marton,0.5 已经满足条件了,也许应该保持原样..
  • 哪种情况?正如我所写,“假设您的网格是 0.2 0.4 0.6 ...”
  • 如果数字是负数,你需要减去 0.5 而不是加 0.5
【解决方案2】:

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

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

我想你可以测试一下这对你是否可靠?

【讨论】:

  • 不是 /= pow(10, p) 还是 *= pow(10, -p)?
【解决方案3】:

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

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

我实际上也在四处寻找这个,但找不到任何东西,所以我拼凑了一个 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

这也是一个 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;
}

输出:

0.1
0
0.6
0.5

0.1
0
1
1

1.23
1.03e+06
0.00923

【讨论】:

    【解决方案4】:

    您可以使用的算法:

    • 获得 10 次幂(有效位数)(=P10)
    • 将双值乘以 P10
    • 加:0.5(如果为负,则减 - 参见 Ankush Shah 的评论)
    • 将此总和的整数部分除以 (P10) - 答案将是您的四舍五入数

    【讨论】:

    • 小点:如果是负数,就要减去0.5
    【解决方案5】:

    使用floor()ceil()floor 会将浮点数转换为下一个较小的整数,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】:

    通常您在编译时就知道所需的精度。因此,使用可用的模板化 Pow 函数here,您可以:

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

    经过测试here

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

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

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

        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
        

        【讨论】:

          【解决方案9】:

          我曾经写过一个小函数,它可以将doubles 舍入到一个固定的精度(小数点后 2 位,比如0.01),而无需包含&lt;cmath&gt;

          constexpr double simplyRounded( const double value )
          {
              return (((((int)(value * 1000.0) % 10) >= 5) + ((int)(value * 100.0))) / 100.0);
          }   
          

          ...可以调整为给你一个小数的精度(如0.1),取并返回float,如下所示:

          constexpr float simplyRounded( const float value )
          {
              return (((((int)(value * 1000.0f) % 10) >= 5) + ((int)(value * 100.0f))) / 100.0f);
          }
          
          

          【讨论】:

            【解决方案10】:

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

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

            【讨论】:

              【解决方案11】:

              浮点数舍入算法:

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

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2020-06-09
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多