【问题标题】:Checking if float is an integer检查浮点数是否为整数
【发布时间】:2011-08-13 09:50:21
【问题描述】:

如何检查float 变量是否包含整数值?到目前为止,我一直在使用:

float f = 4.5886;
if (f-(int)f == 0)
     printf("yes\n");
else printf("no\n");

但我想知道是否有更好的解决方案,或者这个解决方案是否有任何(或许多)缺点。

【问题讨论】:

  • 当数字大于最大允许整数值时,您的方法将失败。
  • 查看我的答案以解决 OP 方法的问题。
  • 当然正确的答案是:你问错问题了。
  • @John Marshall:那么,正确的问题是什么?
  • 嗯,你想做什么?如果您将值存储在float 中,可能是因为您想对其进行浮点运算。那么你的问题就变成了:这个变量,其值在特定领域的方式和取决于你做算术的仔细程度有些蓬松,是否有一个蓬松的积分?要有意义地回答这个问题,您需要考虑特定领域的蓬松度。或者,换一种说法:在这种情况下,正确的方法不是将您的值存储在适当大小的int 中是什么情况?

标签: c floating-point int


【解决方案1】:

stdlib float modf(float x, float *ipart) 分成两部分,检查返回值(小数部分)是否== 0。

【讨论】:

  • 我一直在想,如果与我最初的解决方案相比,这是否不太贵(正如@R.. 在另一个类似的答案中所说)。
  • @mmutz:我说过我应该戴眼镜吗?
  • 当号码为 23.9999999999 时是否有效?在我看来,这仅在数字为 24.0000000000001 时才有效
【解决方案2】:
if (fmod(f, 1) == 0.0) {
  ...
}

别忘了math.hlibm

【讨论】:

  • 这可行,但原则上fmod 是一个相当昂贵的操作。
【解决方案3】:
if (f <= LONG_MIN || f >= LONG_MAX || f == (long)f) /* it's an integer */

【讨论】:

  • 如果sizeof(f) &gt; sizeof(long) 这可能不成立。
  • 只是指出以防有人找到此答案并尝试将其应用于double
  • 好点。对于double,我会使用long longLLONG_MAX/LLONG_MIN
  • @mangledorf:您的转换无效。如果值不能表示为 long(long)f 将调用未定义的行为。因此,为了安全起见,您必须先进行比较。
  • @EmileCormier:作为一个数字,-1.1 * 10^20 无论如何都是一个整数,所以我猜你的意思是不同的。然而,float 的精度受到限制,因此最接近该数字的可表示值是 -109999998686059298816,它也是一个整数。由于float 有一个 24 位的有效位,任何幅度至少为 2^24 的值在小数点之后没有有效位。 C 要求 long 至少为 32 位,因此大于 LONG_MAX 或小于 LONG_MIN 的值远远超出该范围。
【解决方案4】:

除了已经给出的很好的答案,您还可以使用ceilf(f) == ffloorf(f) == f。如果f 是整数,则两个表达式都返回true。它们还为 NaN (NaNs always compare unequal) 返回false,为±infinity 返回true,并且没有溢出用于保存截断结果的整数类型的问题,因为floorf()/ceilf() return @ 987654330@s.

【讨论】:

  • 嗯,这违背了目的。正如标题所说,我想要检查浮点数是否为整数;而不是四舍五入。在后一种情况下,我总是有一个整数。但也感谢您指出这一点。
  • @sidyll:两个表达式都围绕f,是的,但它们这样做是为了检查它是否是一个整数。
  • @sidyll:更准确地说:注意与f相比,舍入操作的结果如何,我已经编辑了我的答案以更清楚地说明这一点,谢谢。
  • 虽然这可行,但由于需要更改和恢复舍入模式,ceilffloorf 在至少某些拱门(主要是 x86)上是不必要的昂贵操作。更快的测试是rintf(f)==f。使用-fno-math-errno,GCC 会将rintf 编译为一条内联指令。
  • @R.. 我是否正确假设 nearbyintf 会在没有异常或标志的情况下做同样的事情?
【解决方案5】:

我不是 100% 确定,但是当您将 f 转换为 int 并从 f 中减去它时,我相信它会被转换回浮点数。在这种情况下,这可能无关紧要,但如果您出于某种原因期望它是一个 int,它可能会出现问题。

我不知道这本身是否是一个更好的解决方案,但您可以改用模数数学,例如: float f = 4.5886; bool isInt; isInt = (f % 1.0 != 0) ? false : true; 根据您的编译器,您可能需要或不需要 1 之后的 .0,整个隐式转换再次发挥作用。在这段代码中,如果小数点右边全为零,bool isInt 应该为真,否则为假。

【讨论】:

  • % 运算符仅适用于整数。不过,您对 int 被强制转换为浮点数是正确的。
  • 我的错误,我应该使用 (fmod(f , 1.0) != 0) 代替。
  • 是的,我知道它会保持浮动,但我只是想检查一下。
  • 不能在 gcc 上编译
【解决方案6】:
#define twop22 (0x1.0p+22)
#define ABS(x) (fabs(x))
#define isFloatInteger(x) ((ABS(x) >= twop22) || (((ABS(x) + twop22) - twop22) == ABS(x)))

【讨论】:

    【解决方案7】:

    请记住,这里的大多数技术都是有效的,假设先前计算导致的舍入误差不是一个因素。例如。你可以使用roundf,像这样:

    float z = 1.0f;
    
    if (roundf(z) == z) {
        printf("integer\n");
    } else {
        printf("fraction\n");
    }
    

    这种技术和其他类似技术(例如ceilf、强制转换为long 等)的问题在于,虽然它们对整数常量很有效,但如果数字是由受浮点舍入误差影响的计算。例如:

    float z = powf(powf(3.0f, 0.05f), 20.0f);
    
    if (roundf(z) == z) {
        printf("integer\n");
    } else {
        printf("fraction\n");
    }
    

    打印“分数”,即使 (31/20)20 应该等于 3,因为实际计算结果最终是 2.9999992847442626953125.

    任何类似的方法,无论是fmodf 还是其他,都受此约束。在执行复杂或容易舍入计算的应用程序中,通常您想要做的是为构成“整数”的内容定义一些“容差”值(这通常适用于浮点相等比较)。我们经常将这种容差称为epsilon。例如,假设我们将原谅计算机最多 +/- 0.00001 的舍入误差。然后,如果我们正在测试z,我们可以选择 0.00001 的 epsilon 并执行以下操作:

    if (fabsf(roundf(z) - z) <= 0.00001f) {
        printf("integer\n");
    } else {
        printf("fraction\n");
    }
    

    你真的不想在这里使用ceilf,因为例如ceilf(1.0000001) 是 2 而不是 1,ceilf(-1.99999999) 是 -1 而不是 -2。

    如果您愿意,可以使用rintf 代替roundf

    选择适合您应用的公差值(是的,有时零公差是合适的)。欲了解更多信息,请查看comparing floating-point numbers 上的这篇文章。

    【讨论】:

      【解决方案8】:

      这涉及计算舍入。您可以根据需要设置 epsilon:

      bool IsInteger(float value)
      {
          return fabs(ceilf(value) - value) < EPSILON;
      }
      

      【讨论】:

        猜你喜欢
        • 2013-11-29
        • 2013-02-25
        • 1970-01-01
        • 1970-01-01
        • 2013-12-26
        • 2023-03-14
        • 2015-12-24
        • 1970-01-01
        相关资源
        最近更新 更多