检查浮点是否为整数的最佳方法

Best way of checking if a floating point is an integer

本文关键字:最佳 方法 整数 是否 检查      更新时间:2023-10-16

[关于这个问题有几个问题,但没有一个答案是特别确定的,有几个与当前的C++标准过时]。

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

  1. if (f >= std::numeric_limits<T>::min() && f <= std::numeric_limits<T>::max() && f == (T)f))

  2. 使用 std::fmod 提取余数并检验 0 的相等性。

  3. 使用 std::remainder 并检验相等性为 0。

第一个测试假定定义了从 fT实例的转换。例如,对于std::int64_t float则不然。

有了C++11,哪一个最好?有没有更好的方法?

结论:

答案是使用std::trunc(f) == f在比较所有这些方法时,时差微不足道。即使我们在下面的示例中编写的特定IEEE展开代码在技术上是快两倍,我们也只说快了1纳秒

从长远来看,维护成本会高得多。因此,使用维护者更容易阅读和理解的解决方案更好。

对一组随机数字完成 12,000,000 次操作的时间(以微秒为单位(:

  • IEEE细分:                                               18
  •                                   std::trunc(f) == f 32
  •                 std::floor(val) - val == 0 35
  •                   ((uint64_t)f) - f) == 0.0 38
  •                      std::fmod(val, 1.0) == 0 87

结论的解决。

浮点数由两部分组成:

mantissa:      The data part of the value.
exponent:      a power to multiply it by.
such that:
   value =  mantissa * (2^exponent)

所以指数基本上是我们要把"二进制点"移到尾数下多少个二进制数字。正值将其向右移动,负值将其向左移动。如果二进制点右侧的所有数字均为零,则我们有一个整数。

如果我们假设 IEEE 754

我们应该注意,这种表示值被归一化,以便尾数中最高有效位移为 1。由于始终设置此位,因此实际上并未存储(处理器知道它在那里并相应地进行补偿(。

所以:

如果exponent < 0,那么您肯定没有整数,因为它只能表示小数值。如果是exponent >= <Number of bits In Mantissa>那么肯定没有事实部分,它是一个整数(尽管您可能无法将其保存在int中(。

否则,我们必须做一些工作。 如果exponent >= 0 && exponent < <Number of bits In Mantissa>,那么如果mantissa在下半部分全部为零(定义如下(,则您可能表示一个整数。

附加作为归一化的一部分127被添加到指数中(使得在8位指数字段中没有存储负值(。

#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

修改了 main(( 以运行测试:

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

IEEE代码(在这个简单的测试中(似乎击败了截断方法并生成了相同的结果。但是时间是微不足道的。超过 1200 万次通话,我们在 14 毫秒内看到了差异。

使用std::fmod(f, 1.0) == 0.0,其中ffloatdoublelong double。如果您担心在使用 float s 时不需要的浮点促销会产生虚假影响,请使用 1.0f 或更全面的

std::fmod(f, static_cast<decltype(f)>(1.0)) == 0.0

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

如果事实证明f是一个整数,那么尝试强制转换之前,请确保它在所选类型的允许范围内:否则您可能会调用未定义的行为。我看到你已经熟悉std::numeric_limits可以在这里帮助你。

我对使用std::remainder的保留意见可能是(i(我是Luddite,(ii(它在某些部分实现C++11标准的编译器中不可用,例如MSVC12。我不喜欢涉及铸件的解决方案,因为符号隐藏了相当昂贵的操作,您需要提前检查安全性。如果一定要采用你的第一选择,至少把C型演员阵容换成static_cast<T>(f);

这个测试很好:

if (   f >= std::numeric_limits<T>::min()
    && f <= std::numeric_limits<T>::max()
    && f == (T)f))

这些测试不完整:

using std::fmod to extract the remainder and test equality to 0.
using std::remainder and test equality to 0.

它们都无法检查是否定义了到 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;
}

编译(gcc 版本 4.9.1 20140903 (预发行版( (GCC( 在 x86_64 Arch Linux 上使用 -g -O3 -std=gnu++0x( 编译为:

0000000000400800 <_Z9isintegerd>:
  400800:       66 0f 2e 05 10 01 00    ucomisd 0x110(%rip),%xmm0        # 400918 <_IO_stdin_used+0x18>
  400807:       00
  400808:       72 56                   jb     400860 <_Z9isintegerd+0x60>
  40080a:       f2 0f 10 0d 0e 01 00    movsd  0x10e(%rip),%xmm1        # 400920 <_IO_stdin_used+0x20>
  400811:       00
  400812:       66 0f 2e c8             ucomisd %xmm0,%xmm1
  400816:       72 48                   jb     400860 <_Z9isintegerd+0x60>
  400818:       48 83 ec 18             sub    $0x18,%rsp
  40081c:       d9 e8                   fld1
  40081e:       f2 0f 11 04 24          movsd  %xmm0,(%rsp)
  400823:       dd 04 24                fldl   (%rsp)
  400826:       d9 f8                   fprem
  400828:       df e0                   fnstsw %ax
  40082a:       f6 c4 04                test   $0x4,%ah
  40082d:       75 f7                   jne    400826 <_Z9isintegerd+0x26>
  40082f:       dd d9                   fstp   %st(1)
  400831:       dd 5c 24 08             fstpl  0x8(%rsp)
  400835:       f2 0f 10 4c 24 08       movsd  0x8(%rsp),%xmm1
  40083b:       66 0f 2e c9             ucomisd %xmm1,%xmm1
  40083f:       7a 22                   jp     400863 <_Z9isintegerd+0x63>
  400841:       66 0f ef c0             pxor   %xmm0,%xmm0
  400845:       31 c0                   xor    %eax,%eax
  400847:       ba 00 00 00 00          mov    $0x0,%edx
  40084c:       66 0f 2e c8             ucomisd %xmm0,%xmm1
  400850:       0f 9b c0                setnp  %al
  400853:       0f 45 c2                cmovne %edx,%eax
  400856:       48 83 c4 18             add    $0x18,%rsp
  40085a:       c3                      retq
  40085b:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)
  400860:       31 c0                   xor    %eax,%eax
  400862:       c3                      retq
  400863:       f2 0f 10 0d bd 00 00    movsd  0xbd(%rip),%xmm1        # 400928 <_IO_stdin_used+0x28>
  40086a:       00
  40086b:       e8 20 fd ff ff          callq  400590 <fmod@plt>
  400870:       66 0f 28 c8             movapd %xmm0,%xmm1
  400874:       eb cb                   jmp    400841 <_Z9isintegerd+0x41>
  400876:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  40087d:       00 00 00

前五条指令实现了针对std::numeric_limits<int>::min()std::numeric_limits<int>::max()的范围检查。 剩下的就是fmod测试,考虑了单次调用fprem指令(400828..40082d(的所有不当行为,以及以某种方式出现NaN的一些情况。

通过使用 remainder 获得类似的代码。

需要考虑的其他一些选项(不同的编译器/库可能会为这些测试生成不同的内在序列,并且更快/更慢(:

bool is_int(float f) { return floor(f) == f; }

这是除了您拥有的溢出测试之外...

如果你想真正优化,你可以尝试以下方法(适用于正浮点数,未经彻底测试(: 这假设 IEEE 32 位浮点数,这不是C++标准 AFAIK 强制要求的。

bool is_int(float f)
{
    const float nf = f + float(1 << 23);
    const float bf = nf - float(1 << 23);
    return f == bf;
}
我会

深入研究 IEE 754 标准并继续只考虑这种类型的问题,我将假设 64 位整数和双精度。

该数字是整数:

  1. 数字为零(与符号无关(。
  2. 该数字使尾数不会变成二进制分数(无论在唱歌上(,同时对于最低有效位没有任何未定义的数字。

我做了以下功能:

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

该方法中的条件不是很清楚,因此我发布了带有注释的if/else结构的不太模糊的版本。

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) %llxn", 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;
}

个人建议使用 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;
}

它不涉及强制转换和浮点运算,这两者都可能是误差的来源。如果浮点值根据 IEEE 754 标准表示,至少通过将尾数的相应位设置为零,肯定可以在不引入数字错误的情况下截断小数位。

就我个人而言,我会犹豫是否使用 fmodremainder 来检查f是否是积分,因为我不确定结果是否可以下溢到零,从而伪造一个整数值。无论如何,更容易证明trunc工作没有数字误差。

上述三种方法都没有实际检查浮点数f是否可以表示为 T 类型的值。需要额外的检查。

第一个选项实际上正是这样做的:它检查f是否是整数并且可以表示为类型 T 的值。它通过评估f == (T)f来实现。此检查涉及强制转换。根据 C++11 标准第 4.9 节中的 §1"如果截断的值无法在目标类型中表示",则未定义此类强制转换。因此,如果f大于或等于std::numeric_limits<T>::max()+1则截断的值肯定会因此具有未定义的行为。

这可能就是为什么第一个选项在执行强制转换之前有一个额外的范围检查(f >= std::numeric_limits<T>::min() && f <= std::numeric_limits<T>::max()(。这个范围检查也可以用于其他方法(truncfmodremainder(,以确定f是否可以表示为T类型的值。但是,检查是有缺陷的,因为它可能会遇到未定义的行为:在此检查中,限制std::numeric_limits<T>::min/max()转换为浮点类型,以应用相等运算符。例如,如果T=uint32_tf是一个floatstd::numeric_limits<T>::max()不能表示为浮点数。然后,C++11标准在第4.9 §2节中指出,实现可以自由选择下一个更低或更高的可表示值。如果它选择较高的可表示值,并且f恰好等于较高的可表示值,则根据第 4.9 节中的 §1 未定义后续转换,因为(截断的(值不能在目标类型 (uint32_t( 中表示。

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.

因此,第一种选择将确定f是整体的,即使不是,也可以表示为uint32_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);
}

然后,您终于可以安全地进行石膏转换:

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

这样转换类型怎么样?

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

问题与:

  if (   f >= std::numeric_limits<T>::min()
      && f <= std::numeric_limits<T>::max()
      && f == (T)f))

是如果 T 是(例如(64 位,那么在转换为您通常的 64 位双精度时,最大值将四舍五入:-( 假设 2 的补码,当然,最小

值并非如此。

因此,根据尾数中的位数和 T 中的位数,您需要屏蔽 std::numeric_limits::max((的 LS 位... 对不起,我不做C++,所以如何最好地做这件事我留给别人。 [在 C 中,它将类似于 LLONG_MAX ^ (LLONG_MAX >> DBL_MANT_DIG) - 假设 T 是 long long int 的,f 是double的,并且它们都是通常的 64 位值。

如果 T 是常数,那么 min 和 max 的两个浮点值的构造将(我假设(在编译时完成,因此这两个比较非常简单。 你真的不需要能够漂浮T...但是你确实需要知道它的最小值和最大值将适合一个普通整数(例如长整型(。

剩下的工作是将浮点数转换为整数,然后再次将其浮动以进行最终比较。 因此,假设 f 在范围内(保证 (T(f 不会溢出(:

  i  = (T)f ;         // or i  = (long long int)f ;
  ok = (i == f) ;

另一种选择似乎是:

  i  = (T)f ;         // or i  = (long long int)f ;
  ok = (floor(f) == f) ;

正如其他地方所指出的。 它用floor(f)取代了i的浮动......我不相信这是一种改进。

如果 f 是 NaN,事情可能会出错,所以你可能也想测试一下。

您可以尝试用frexp()打开f包装并将尾数提取为(例如(一个长长的 int(带有ldexp()和石膏(,但是当我开始勾勒出来时,它看起来很丑:-(


睡了一觉之后,处理最大问题的更简单方法是:min <= f < ((unsigned)max+1) - 或min <= f < (unsigned)min - 或(double)min <= f < -(double)min - 或任何其他构造-2^(n-1(和+2^(n-1(作为浮点值的方法,其中n是T中的位数。

(为我在凌晨 1:00 对问题感兴趣是正确的!

首先,我想看看我是否正确回答了你的问题。从我所读到的内容来看,您似乎想要确定浮点数是否实际上只是浮点中整数类型的表示形式。

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

template<typename F, typename I = size_t>
bool is_integral(F f)
{
  return fabs(f - static_cast<I>(f)) <= std::numeric_limits<F>::epsilon;
}

这个想法是简单地找到原始浮点数和转换为整型的浮点数之间的绝对差值,然后确定它是否小于浮点类型的 epsilon。我在这里假设,如果它比ε小,差异对我们来说并不重要。

感谢您的阅读。

使用 modf() 将值分解为整数部分和分数部分。 从这个直接测试中,知道double是否是整数。 在此之后,可以针对目标整数类型的最小值/最大值进行极限测试。

#include <cmath>
bool IsInteger(double x) {
  double ipart;
  return std::modf(x, &ipart) == 0.0;  // Test if fraction is 0.0.
}

注意 modf() 与类似的命名fmod()不同。

在 OP 发布的 3 种方法中,转换为整数/从整数转换可能会执行相当多的工作来执行转换和比较。 其他 2 个略有相同。 假设除以 1.0 不会产生意外的舍入模式影响,它们就可以工作。 但是做一个不必要的划分。

至于哪个最快可能取决于所使用的double的组合。

OP的第

一种方法有一个独特的优势:由于目标是测试FP是否可以精确地转换为某个整数,并且如果结果为真,则可能需要进行转换,因此OP的第一种方法已经完成了转换。

这是我会尝试的:

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

如果你的问题是"我可以在不丢失信息的情况下将此双精度转换为 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