检查浮点是否为整数的最佳方法 有一些问题,但没有一个答案是特别明确的,而且有几个C++的标准过时了。

检查浮点是否为整数的最佳方法 有一些问题,但没有一个答案是特别明确的,而且有几个C++的标准过时了。,c++,c++11,floating-point,floating-point-conversion,C++,C++11,Floating Point,Floating Point Conversion,我的研究表明,这些是检查浮点值是否可以转换为整数类型T的主要方法 如果(f>=std::numeric_limits::min()&&f如果您的问题是“我可以在不丢失信息的情况下将这个double转换为int吗?”那么我会做一些简单的事情,如: template <typename T, typename U> bool CanConvert(U u) { return U(T(u)) == u; } CanConvert<int>(1.0) -- true Can

我的研究表明,这些是检查浮点值是否可以转换为整数类型
T
的主要方法

  • 如果(f>=std::numeric_limits::min()&&f如果您的问题是“我可以在不丢失信息的情况下将这个double转换为int吗?”那么我会做一些简单的事情,如:

    template <typename T, typename U>
    bool CanConvert(U u)
    {
      return U(T(u)) == u;
    }
    
    CanConvert<int>(1.0) -- true
    CanConvert<int>(1.5) -- false
    CanConvert<int>(1e9) -- true
    CanConvert<int>(1e10)-- false
    
    模板
    布尔坎康沃尔特(U)
    {
    返回U(T(U))==U;
    }
    CanConvert(1.0)--正确
    CanConvert(1.5)-错误
    CanConvert(1e9)——正确
    CanConvert(1e10)-错误
    
    这个测试很好:

    if (   f >= std::numeric_limits<T>::min()
        && f <= std::numeric_limits<T>::max()
        && f == (T)f))
    
    它们都无法检查是否定义了到
    T
    的转换。溢出整数类型的浮点到整数转换会导致未定义的行为,甚至比舍入更糟糕

    出于另一个原因,我建议避免使用
    std::fmod
    。此代码:

    int isinteger(double d) {
      return std::numeric_limits<int>::min() <= d
          && d <= std::numeric_limits<int>::max()
          && std::fmod(d, 1.0) == 0;
    }
    
    int-isinteger(双d){
    返回std::numeric_limits::min()Use
    std::fmod(f,1.0)==0.0
    其中
    f
    是一个
    float
    double
    long double
    。如果您担心在使用
    float
    s时出现不必要的浮点提升的虚假效果,请使用
    1.0f
    或更全面的选项

    std::fmod(f,静态_-cast(1.0))==0.0

    这将强制在编译时调用正确的重载。
    std::fmod(f,…)
    的返回值将在[0,1]范围内,与
    0.0
    进行比较以完成整数检查是完全安全的

    如果结果显示
    f
    是一个整数,那么在尝试强制转换之前,请确保它在所选类型的允许范围内:否则您可能会调用未定义的行为。我发现您已经熟悉
    std::numeric\u limits
    ,这对您有所帮助


    我对使用
    std::rements
    的保留可能是(i)我是一名勒德分子,以及(ii)在某些部分实现C++11标准的编译器中,例如MSVC12,它不可用。我不喜欢涉及强制转换的解决方案,因为符号隐藏了相当昂贵的操作,您需要提前检查安全性。如果您必须采用第一选择,至少用
    static_cast(f)替换C样式强制转换< /代码>;

    考虑的其他选项(不同编译器/库可能为这些测试产生不同的内在序列,并且速度更快/更快):

    这是对溢出测试的补充

    如果你想真正的优化,你可以尝试以下(积极浮动,不彻底测试):这假设IEEE 32位浮动,这不是由C++标准AFAIK。< /P>

    bool is_int(float f)
    {
        const float nf = f + float(1 << 23);
        const float bf = nf - float(1 << 23);
        return f == bf;
    }
    
    bool是int(浮点f)
    {
    
    const float nf=f+float(1以下是我要尝试的:

    float originalNumber;
    cin >> originalNumber;
    int temp = (int) originalNumber;
    if (originalNumber-temp > 0)
    {
        // It is not an integer
    }
    else
    {
        // It is an integer
    }
    
    问题在于:

      if (   f >= std::numeric_limits<T>::min()
          && f <= std::numeric_limits<T>::max()
          && f == (T)f))
    
    另一种选择似乎是:

      i  = (T)f ;         // or i  = (long long int)f ;
      ok = (floor(f) == f) ;
    
    正如其他地方所指出的。它用
    floor(f)
    取代了
    i
    的浮动…我不认为这是一种改进

    如果f为NaN,则可能会出现问题,因此您可能也需要进行测试

    您可以尝试使用
    frexp()
    解包
    f
    ,并将尾数提取为(比如)长整型(使用
    ldexp()
    和一个cast),但当我开始绘制时,它看起来很难看:-(



    睡过觉之后,处理max问题的一个更简单的方法是:
    min这样转换类型怎么样

    bool can_convert(float a, int i)
    {
      int b = a;
      float c = i;
      return a == c;
    }
    

    我个人建议使用C++11中引入的
    trunc
    函数检查
    f
    是否为整数:

    #include <cmath>
    #include <type_traits>
    
    template<typename F>
    bool isIntegral(F f) {
        static_assert(std::is_floating_point<F>::value, "The function isIntegral is only defined for floating-point types.");
        return std::trunc(f) == f;
    }
    
    因此,第一个选项将确定
    f
    是完整的,可以表示为
    uint32\u t
    ,即使它不是

    一般来说,修正范围检查并不容易。有符号整数和浮点数没有固定的表示形式(如二的补码或IEEE 754)根据标准,不要让事情变得更简单。一种可能是为您使用的特定编译器、体系结构和类型编写不可移植的代码。更可移植的解决方案是使用Boost的NumericConversion库:

    #include <boost/numeric/conversion/cast.hpp>
    
    template<typename T, typename F>
    bool isRepresentableAs(F f) {
        static_assert(std::is_floating_point<F>::value && std::is_integral<T>::value, "The function isRepresentableAs is only defined for floating-point as integral types.");
        return boost::numeric::converter<T, F>::out_of_range(f) == boost::numeric::cInRange && isIntegral(f);
    }
    
    #包括
    模板
    bool isRepresentableAs(F){
    static_assert(std::is_floating_point::value&&std::is_integral::value,“函数isRepresentableAs仅为浮点定义为整数类型”);
    返回boost::numeric::converter::超出范围(f)=boost::numeric::cInRange&&isIntegral(f);
    }
    
    然后,您可以安全地执行强制转换:

    double f = 333.0;
    if (isRepresentableAs<uint32_t>(f))
        std::cout << static_cast<uint32_t>(f) << std::endl;
    else
        std::cout << f << " is not representable as uint32_t." << std::endl;
    // Output: 333
    
    double f=333.0;
    如果(isRepresentableAs(f))
    
    首先,我想看看你的问题是否正确。从我所读到的内容来看,你似乎想确定一个浮点是否实际上只是一个整数类型在浮点中的表示

    据我所知,由于浮点不准确,在浮点上执行
    ==
    是不安全的。因此我提出以下解决方案:

    模板
    布尔积分(F)
    {
    返回晶圆厂(f-静态铸造(f))结论:
    答案是使用
    std::trunc(f)=f
    当比较所有这些方法时,时间差是微不足道的。即使我们在下面的示例中编写的特定IEEE解卷代码在技术上是两倍快,我们只说1纳秒

    但从长远来看,维护成本会明显较高。因此,使用维护人员更容易阅读和理解的解决方案更好

    在一组随机数字上完成12000000次操作的时间(以微秒为单位):

    • IEEE分类:18
    • std::trunc(f)==f
      32
    • std::floor(val)-val==0
      35
    • ((uint64_t)f)-f)=0.0
      38
    • bool can_convert(float a, int i)
      {
        int b = a;
        float c = i;
        return a == c;
      }
      
      #include <cmath>
      #include <type_traits>
      
      template<typename F>
      bool isIntegral(F f) {
          static_assert(std::is_floating_point<F>::value, "The function isIntegral is only defined for floating-point types.");
          return std::trunc(f) == f;
      }
      
      std::cout << std::numeric_limits<uint32_t>::max() << std::endl;  // 4294967295
      std::cout << std::setprecision(20) << static_cast<float>(std::numeric_limits<uint32_t>::max()) << std::endl;  // 4294967296 (float is a single precision IEEE 754 floating point number here)
      std::cout << static_cast<uint32_t>(static_cast<float>(std::numeric_limits<uint32_t>::max())) << std::endl;  // Could be for example 4294967295 due to undefined behavior according to the standard in the cast to the uint32_t.
      
      #include <boost/numeric/conversion/cast.hpp>
      
      template<typename T, typename F>
      bool isRepresentableAs(F f) {
          static_assert(std::is_floating_point<F>::value && std::is_integral<T>::value, "The function isRepresentableAs is only defined for floating-point as integral types.");
          return boost::numeric::converter<T, F>::out_of_range(f) == boost::numeric::cInRange && isIntegral(f);
      }
      
      double f = 333.0;
      if (isRepresentableAs<uint32_t>(f))
          std::cout << static_cast<uint32_t>(f) << std::endl;
      else
          std::cout << f << " is not representable as uint32_t." << std::endl;
      // Output: 333
      
      mantissa:      The data part of the value.
      exponent:      a power to multiply it by.
      
      such that:
      
         value =  mantissa * (2^exponent)
      
      #include <limits>
      #include <iostream>
      #include <cmath>
      
      /*
       *  Bit  31      Sign
       *  Bits 30-23   Exponent
       *  Bits 22-00   Mantissa
       */
      bool is_IEEE754_32BitFloat_AnInt(float val)
      {
          // Put the value in an int so we can do bitwise operations.
          int  valAsInt = *reinterpret_cast<int*>(&val);
      
          // Remember to subtract 127 from the exponent (to get real value)
          int  exponent = ((valAsInt >> 23) & 0xFF) - 127;
      
          int bitsInFraction = 23 - exponent;
          int mask = exponent < 0
                          ? 0x7FFFFFFF
                          : exponent > 23
                               ? 0x00
                               : (1 << bitsInFraction) - 1;
      
          return !(valAsInt & mask);
      }
      /*
       *  Bit  63      Sign
       *  Bits 62-52   Exponent
       *  Bits 51-00   Mantissa
       */
      bool is_IEEE754_64BitFloat_AnInt(double val)
      {
          // Put the value in an long long so we can do bitwise operations.
          uint64_t  valAsInt = *reinterpret_cast<uint64_t*>(&val);
      
          // Remember to subtract 1023 from the exponent (to get real value)
          int  exponent = ((valAsInt >> 52) & 0x7FF) - 1023;
      
          int bitsInFraction = 52 - exponent;
          uint64_t mask = exponent < 0
                          ? 0x7FFFFFFFFFFFFFFFLL
                          : exponent > 52
                              ? 0x00
                              : (1LL << bitsInFraction) - 1;
      
          return !(valAsInt & mask);
      }
      
      bool is_Trunc_32BitFloat_AnInt(float val)
      {
          return (std::trunc(val) - val == 0.0F);
      }
      
      bool is_Trunc_64BitFloat_AnInt(double val)
      {
          return (std::trunc(val) - val == 0.0);
      }
      
      bool is_IntCast_64BitFloat_AnInt(double val)
      {
          return (uint64_t(val) - val == 0.0);
      }
      
      template<typename T, bool isIEEE = std::numeric_limits<T>::is_iec559>
      bool isInt(T f);
      
      template<>
      bool isInt<float, true>(float f) {return is_IEEE754_32BitFloat_AnInt(f);}
      
      template<>
      bool isInt<double, true>(double f) {return is_IEEE754_64BitFloat_AnInt(f);}
      
      template<>
      bool isInt<float, false>(float f) {return is_Trunc_64BitFloat_AnInt(f);}
      
      template<>
      bool isInt<double, false>(double f) {return is_Trunc_64BitFloat_AnInt(f);}
      
      int main()
      {
          double  x = 16;
          std::cout << x << "=> " << isInt(x) << "\n";
      
          x = 16.4;
          std::cout << x << "=> " << isInt(x) << "\n";
      
          x = 123.0;
          std::cout << x << "=> " << isInt(x) << "\n";
      
          x = 0.0;
          std::cout << x << "=> " << isInt(x) << "\n";
      
          x = 2.0;
          std::cout << x << "=> " << isInt(x) << "\n";
      
          x = 4.0;
          std::cout << x << "=> " << isInt(x) << "\n";
      
          x = 5.0;
          std::cout << x << "=> " << isInt(x) << "\n";
      
          x = 1.0;
          std::cout << x << "=> " << isInt(x) << "\n";
      }
      
      > ./a.out
      16=> 1
      16.4=> 0
      123=> 1
      0=> 1
      2=> 1
      4=> 1
      5=> 1
      1=> 1
      
      (for a in {1..3000000};do echo $RANDOM.$RANDOM;done ) > test.data
      (for a in {1..3000000};do echo $RANDOM;done ) >> test.data
      (for a in {1..3000000};do echo $RANDOM$RANDOM0000;done ) >> test.data
      (for a in {1..3000000};do echo 0.$RANDOM;done ) >> test.data
      
      int main()
      {
          // ORIGINAL CODE still here.
          // Added this trivial speed test.
      
          std::ifstream   testData("test.data");  // Generated a million random numbers
          std::vector<double>  test{std::istream_iterator<double>(testData), std::istream_iterator<double>()};
          std::cout << "Data Size: " << test.size() << "\n";
          int count1 = 0;
          int count2 = 0;
          int count3 = 0;
      
          auto start = std::chrono::system_clock::now();
          for(auto const& v: test)
          {   count1 += is_IEEE754_64BitFloat_AnInt(v);
          }
          auto p1 = std::chrono::system_clock::now();
          for(auto const& v: test)
          {   count2 += is_Trunc_64BitFloat_AnInt(v);
          }
          auto p2 = std::chrono::system_clock::now();
          for(auto const& v: test)
          {   count3 += is_IntCast_64BitFloat_AnInt(v);
          }
      
          auto end = std::chrono::system_clock::now();
      
          std::cout << "IEEE  " << count1 << " Time: " << std::chrono::duration_cast<std::chrono::milliseconds>(p1 - start).count() << "\n";
          std::cout << "Trunc " << count2 << " Time: " << std::chrono::duration_cast<std::chrono::milliseconds>(p2 - p1).count()    << "\n";
          std::cout << "Int Cast " << count3 << " Time: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - p2).count()   << "\n";    }
      
      > ./a.out
      16=> 1
      16.4=> 0
      123=> 1
      0=> 1
      2=> 1
      4=> 1
      5=> 1
      1=> 1
      Data Size: 12000000
      IEEE  6000199 Time: 18
      Trunc 6000199 Time: 32
      Int Cast 6000199 Time: 38
      
      #include <cmath>
      
      bool IsInteger(double x) {
        double ipart;
        return std::modf(x, &ipart) == 0.0;  // Test if fraction is 0.0.
      }
      
      #include <stdio.h>
      
      int IsThisDoubleAnInt(double number)
      {
          long long ieee754 = *(long long *)&number;
          long long sign = ieee754 >> 63;
          long long exp = ((ieee754 >> 52) & 0x7FFLL);
          long long mantissa = ieee754 & 0xFFFFFFFFFFFFFLL;
          long long e = exp - 1023;
          long long decimalmask = (1LL << (e + 52));
          if (decimalmask) decimalmask -= 1;
          if (((exp == 0) && (mantissa != 0)) || (e > 52) || (e < 0) || ((mantissa & decimalmask) != 0))
          {
              return 0;
          }
          else
          {
              return 1;
          }
      }
      
      int main()
      {
          double x = 1;
          printf("x = %e is%s int.\n", x, IsThisDoubleAnInt(x)?"":" not");
          x = 1.5;
          printf("x = %e is%s int.\n", x, IsThisDoubleAnInt(x)?"":" not");
          x = 2;
          printf("x = %e is%s int.\n", x, IsThisDoubleAnInt(x)?"":" not");
          x = 2.000000001;
          printf("x = %e is%s int.\n", x, IsThisDoubleAnInt(x)?"":" not");
          x = 1e60;
          printf("x = %e is%s int.\n", x, IsThisDoubleAnInt(x)?"":" not");
          x = 1e-60;
          printf("x = %e is%s int.\n", x, IsThisDoubleAnInt(x)?"":" not");
          x = 1.0/0.0;
          printf("x = %e is%s int.\n", x, IsThisDoubleAnInt(x)?"":" not");
          x = x/x;
          printf("x = %e is%s int.\n", x, IsThisDoubleAnInt(x)?"":" not");
          x = 0.99;
          printf("x = %e is%s int.\n", x, IsThisDoubleAnInt(x)?"":" not");
          x = 1LL << 52;
          printf("x = %e is%s int.\n", x, IsThisDoubleAnInt(x)?"":" not");
          x = (1LL << 52) + 1;
          printf("x = %e is%s int.\n", x, IsThisDoubleAnInt(x)?"":" not");
      }
      
      x = 1.000000e+00 is int.
      x = 1.500000e+00 is not int.
      x = 2.000000e+00 is int.
      x = 2.000000e+00 is not int.
      x = 1.000000e+60 is not int.
      x = 1.000000e-60 is not int.
      x = inf is not int.
      x = nan is not int.
      x = 9.900000e-01 is not int.
      x = 4.503600e+15 is int.
      x = 4.503600e+15 is not int.
      
      int IsThisDoubleAnIntWithExplanation(double number)
      {
          long long ieee754 = *(long long *)&number;
          long long sign = ieee754 >> 63;
          long long exp = ((ieee754 >> 52) & 0x7FFLL);
          long long mantissa = ieee754 & 0xFFFFFFFFFFFFFLL;
          if (exp == 0)
          {
              if (mantissa == 0)
              {
                  // This is signed zero.
                  return 1;
              }
              else
              {
                  // this is a subnormal number
                  return 0;
              }
          }
          else if (exp == 0x7FFL)
          {
              // it is infinity or nan.
              return 0;
          }
          else
          {
              long long e = exp - 1023;
              long long decimalmask = (1LL << (e + 52));
              if (decimalmask) decimalmask -= 1;
              printf("%f: %llx (%lld %lld %llx) %llx\n", number, ieee754, sign, e, mantissa, decimalmask);
              // number is something in form (-1)^sign x 2^exp-1023 x 1.mantissa
              if (e > 63)
              {
                  // number too large to fit into integer
                  return 0;
              }
              else if (e > 52)
              {
                  // number too large to have all digits...
                  return 0;
              }
              else if (e < 0)
              {
                  // number too large to have all digits...
                  return 0;
              }
              else if ((mantissa & decimalmask) != 0)
              {
                  // number has nonzero fraction part.
                  return 0;
              }
          }
          return 1;
      }