【问题标题】:Standards compliant way to compare float to integral?将浮点数与积分进行比较的符合标准的方法?
【发布时间】:2015-07-05 08:27:24
【问题描述】:

假设我有两个对象if,分别是IF。我知道std::is_integral<I>::value 是真的,std::is_floating_point<F>::value 是真的。

是否有一种完全符合标准的方法来确定i 的值是否小于f 的值?请注意对“完全符合标准”的强调,对于这个问题,我只对由 C++ 标准保证支持的答案感兴趣。


简单的实现i < I(f) 不起作用,因为f 的值可能不适合i。简单的实现F(i) < f 也不起作用,因为f 的精度可能不足以表示i,导致i 被四舍五入到等于f 的值(如果你有IEEE754 浮点数, 16777219 < 16777220.f 失败)。

但真正的困境来了:如果你想使用std::numeric_limits::max 来缓解这些问题,你就回到了比较浮点数和整数的原始问题!这是因为std::numeric_limits::max的类型和原来的类型是一样的。

【问题讨论】:

  • 这个问题的要求“我只对由 C++ 标准保证支持的答案感兴趣”对于这个问题来说是不合理和愚蠢的。这类似于寻求一种实现冒泡排序的方法,并要求答案得到 C++ 标准的支持。 C++ 标准对冒泡排序没有任何规定。
  • i < f 是一种“符合标准”的方式来确定i 是否小于f
  • @Cheersandhth.-Alf 我不同意。问题的全部意义在于找到一种在任何 C++ 实现上都定义良好的方法。您可以为冒泡排序执行此操作,但我不确定您是否可以进行此比较,因此是我的问题。
  • @Barry 然而,我的问题中的反例却失败了——所以这显然是不对的。
  • @orlp:不确定您是否理解您的要求。例如你知道标准支持十进制浮点表示吗?你想要那个覆盖吗?您是否知道标准(很可能是一次事故)不需要对称整数范围?你想要那个覆盖吗?你知道标准甚至没有定义例如乘法?你想要那个覆盖吗?简而言之,“在任何 [exant] C++ 实现上都定义良好的方法”与正式保证之间有很长的距离。

标签: c++ floating-point integer comparison language-lawyer


【解决方案1】:

我会这样做:

我假设 f 是有限的,无限和 NaN 的情况将在其他地方处理。

  1. 比较 f 和 F(i),如果不相等,你就完成了,f 和 i 要么是

  2. 如果相等,则比较I(f)和i

唯一的假设是:

  • 如果有一个浮点数恰好具有 i 值,则 F(i) 给出该值

  • 如果有一个整数的值与 f 完全相同,则 I(f) 给出该值

  • 函数 F 和 I 的单调性

编辑

更明确地说,以上技巧用于编写比较函数,而不仅仅是测试相等性...

floatType F(intType i);
intType I(floatType f);
int cmpfi(floatType f,intType i)
{
  assert(isfinite(f));
  if(f < F(i)) return -1;
  if(f == F(i))
  {
    if( I(f) < i ) return -1;
    return I(f) > i;
  }
  return 1;
}

由您将此草案转换为可以处理多种不同 floatType/intType 的 C++ 代码

【讨论】:

  • 我正在寻找排序测试,而不是相等性。
  • @orlp,我没有详细回答,但这些提示是为了编写一种 cmp 函数
  • 好吧,在第 1 步中,您在发现 f != i 后说“您完成了”。这显然不能解决问题:)
  • @orlp,好的,更多细节可以帮助您推断我的想法
  • 我没有接受,因为我发现了the exact requirements of the standard - 它非常接近,但在std::numeric_limits&lt;I&gt;::max() 周围略有不同。
【解决方案2】:
  1. 如果fI的范围之外,你可以通过它的符号来判断结果。

  2. 如果fI 的范围内但太大而不能有小数部分,则将其作为整数进行比较。

  3. 1234563 /p>

.

template< typename I, typename F >
std::enable_if_t< std::is_integral_v< I > && std::is_floating_point_v< F >,
bool > less( I i, F f ) {
    // Return early for operands of different signs.
    if ( i < 0 != f < 0 ) return i < 0;

    bool rev = i >= 0;
    if ( rev ) {
        f = - f; // Make both operands non-positive.
        i = - i; // (Negativity avoids integer overflow here.)
    }

    if ( f < /* (F) */ std::numeric_limits<I>::min() ) {
        // |i| < |f| because f is outside the range of I.
        return rev;
    }
    if ( f * std::numeric_limits<F>::epsilon() <= -1 ) {
        // f must be an integer (in I) because of limited precision in F.
        I fi = f;
        return rev? fi < i : i < fi;
    }
    // |f| has better than integer precision.
    // If (F) |i| loses precision, it will still be greater than |f|.
    return rev? f < i : i < f;
}

演示:http://coliru.stacked-crooked.com/a/b5c4bea14bc09ee7

【讨论】:

  • ... 嗯,此代码仅适用于 signed 整数。您可以通过剥离所有符号业务来为无符号整数添加重载。
猜你喜欢
  • 1970-01-01
  • 2013-11-19
  • 1970-01-01
  • 2021-05-28
  • 2017-08-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-29
相关资源
最近更新 更多