【问题标题】:Truncate floor into three decimal point C++将地板截断为小数点后三位 C++
【发布时间】:2014-01-02 21:00:34
【问题描述】:

我想将楼层号截断为 3 位十进制数。示例:

input : x = 0.363954;
output: 0.364

我用过

double myCeil(float v, int p)
{
  return int(v * pow(float(10),p))/pow(float(10),p );
}

但输出是 0.3630001 。 我尝试使用<cmath> 中的trunc,但它不存在。

【问题讨论】:

  • 你#include <cmath>了吗?
  • 你的平台是什么? 应该包括 std::floor
  • 我听说有些版本的 Visual C 没有它。你在使用 MSVC 吗?
  • @Tim 是的,我包括了它。
  • @polkadotcadaver std::floor 也不存在。

标签: c++ decimal truncate


【解决方案1】:

您要查找的函数是std::ceil,而不是std::trunc

    double myCeil(double v, int p)
    {
        return std::ceil(v * std::pow(10, p)) / std::pow(10, p);
    }

根据需要将std::floor 或std::round 替换为myFloor 或myRound。 (请注意,std::round 出现在 C++11 中,如果尚未启用,则必须启用它)。

【讨论】:

  • 使用ceil 将0.363954 变为0.36399999
  • @Misaki 这是因为0.363999999 是双精度可以表示的最接近0.364 的值。
  • 好吧,我还是有同样的问题。
  • @Misaki 如果你期待完美的十进制算术,那么 double 帮不了你。
【解决方案2】:

浮点数学通常使用二进制表示;因此,存在无法精确表示为浮点值的十进制值。试图摆弄内部精度恰好遇到了这个问题。但大多数情况下,当有人尝试这样做时,他们实际上是在尝试使用特定精度显示一个值,这很简单:

double x = 0.363954;
std::cout.precision(3);
std::cout << x << '\n';

【讨论】:

  • 我不想显示它,我在比较过程中需要它,使用 (, and ==) 所以它对 0.00001 增加非常敏感。
  • @Misaki - 听起来像是 XY 问题。与其描述您正在考虑的技术,不如描述该技术应该解决的问题。通常有比调整精度更好的方法(主要是因为调整精度很棘手)。
  • 我正在比较图像中两个斑点的圆形度。我通过这段代码float GetCircularity(vector&lt;Point&gt; hull,float area, Point centroid) { float circularity = 0; for(int i = 0 ; i &lt; hull.size() ; i++){ circularity += EuclideanDist(centroid,hull[i]); } return pow(circularity/hull.size(),2) / area; }计算了循环度
  • @Misaki - 这超出了我的专业领域,所以我无法提供任何详细的帮助。但是这个计算看起来已经做了很多次了,很有可能有人发布了一些有用的东西。
【解决方案3】:

精确地得到 0.364 是不可能的。您无法将数字 0.364 (364/1000) 完全存储为浮点数,就像您需要无限个小数才能将 1/3 写为 0.3333333333...

你做对了,除了你可能想使用 std::round() 来舍入到最接近的数字,而不是 int(),它会截断。

比较浮点数是一件棘手的事情。通常,您能做的最好的事情就是检查这些数字是否足够接近。

您是否出于比较目的进行四舍五入?在这种情况下,您似乎对 3 位小数感到满意(这取决于所讨论的每个问题......),在这种情况下,为什么不只是

bool are_equal_to_three_decimals(double a, double b)
{
   return std::abs(a-b) < 0.001;
}

注意,比较四舍五入得到的结果和我建议的函数是不等价的!​​p>

【讨论】:

    【解决方案4】:

    这是一篇旧帖子,但您要求的是二进制数学的十进制精度。两者之间的转换给你一个明显的区别。

    我认为,您所做的主要观点与身份有关,因此您可以在两个数字之间使用相等/不等比较。

    由于我们人类使用的(十进制)和计算机使用的(二进制)之间存在差异,因此我们有三个选择。

    1. 我们使用十进制库。这在计算上是昂贵的,因为我们使用的数学与计算机的工作方式不同。有几个,有朝一日它们可能会被标准采纳。参见例如“ISO/IEC JTC1 SC22 WG21 N2849”

    2. 我们学习用二进制计算。这在精神上代价高昂,因为这不是我们通常做数学的方式。

    3. 我们更改算法以包含身份测试。

    4. 我们更改算法以使用差异测试。

    使用选项 3,我们可以决定一个数字与另一个数字的接近程度才能被视为“相同的数字”。

    这样做的一个简单方法是(如上面@SirGuy 给出的)我们使用天花板或地板作为测试 - 这很好,因为它允许我们选择我们感兴趣的有效位数。它是特定领域,如果使用 2 而不是 10 的幂,他给出的解决方案可能会更优化。

    您肯定只想在使用等式/不等式测试时进行计算。

    所以现在,我们的相等性测试将是(对于 10 个二进制位置(接近 3dp))

    // Normal identity test for floats.
    // Quick but fails eg 1.0000023 == 1.0000024 
    return (a == b);
    

    变成(2^10 = 1024)。

    // Modified identity test for floats.
    // Works with 1.0000023 == 1.0000024 
    return (std::floor(a * 1024) == std::floor(b * 1024));
    

    但这不是很好 我会选择选项4。 假设您认为任何小于 0.001 的差异都是微不足道的,例如 1.00012 = 1.00011。

    这会进行额外的减法和符号移除,这比位移更便宜(也更可靠)。

    // Modified equality test for floats.
    // Returns true if the ∂ is less than 1/10000.
    // Works with 1.0000023 == 1.0000024 
    return abs(a - b) < 0.0001;
    
    

    这归结为您关于计算圆度的评论,我建议您计算两个圆之间的增量(差异),而不是测试等价性。但这并不是您在问题中所问的...

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-04
      • 2019-03-01
      • 2019-04-23
      • 2021-12-21
      相关资源
      最近更新 更多