替代 ceil() 和 floor() 以获得最接近的整数值,高于和低于浮点值?

Alternative to ceil() and floor() to get the closest integer values, above and below of a floating point value?

我正在寻找 C 中 ceil()floor() 函数的替代方法,因为不允许我在项目中使用这些函数。

到目前为止,我构建的是一种棘手的来回方式,通过使用强制转换运算符以及从浮点值(在我的例子中是 double)到 int 后来我需要最接近的整数,高于和低于给定的浮点值,也是 double 值,回到 double:

#include <stdio.h>

int main(void) {
   double original = 124.576;
   double floorint;
   double ceilint;
   int f;
   int c;

   f = (int)original;            //Truncation to closest floor integer value
   c = f + 1;
   floorint = (double)f;
   ceilint = (double)c;

   printf("Original Value: %lf, Floor Int: %lf , Ceil Int: %lf", original, floorint, ceilint);
}

输出:

Original Value: 124.576000, Floor Int: 124.000000 , Ceil Int: 125.000000 

对于此示例,通常我不需要将 cf 的 ceil 和 floor 整数值转换回 double 但我需要它们 double 在我的真实程序中。将其视为任务的要求。


虽然输出给出了所需的值并且到目前为止看起来是正确的,但我仍然担心这种方法是否真的那么正确和合适,或者更清楚地说,如果这种方法确实带来任何不良行为或发布到程序中或与其他替代方案相比给我带来性能损失,如果有任何其他可能的替代方案。


你知道更好的选择吗?如果是这样,为什么这个应该更好?

非常感谢。

你错过了一个重要的步骤:你需要检查数字是否已经是整数,所以对于 ceil 假设非负数(概括是微不足道的),使用类似

double ceil(double f){
    if (f >= LLONG_MAX){
        // f will be integral unless you have a really funky platform
        return f;
    } else {
        long long i = f;
        return 0.0 + i + (f != i); // to obviate potential long long overflow
    }
}

这个谜题中的另一个缺失部分被我封闭的 if 覆盖了,它是检查 f 是否在 long long 的范围内。在通用平台上,如果 flong long 的范围之外,那么无论如何它都是不可分割的。

请注意 floor 是微不足道的,因为 long long 的截断总是趋向于零。

Do you know a better alternative? And if so, why this one should be better?

OP'代码失败:

  • original已经是整数了

  • original-1.5 一样是负数。截断不是在那里。

  • original 刚好超出 int 范围。

  • original 不是数字。


另类构造

double my_ceil(double x)

x 超出整数范围时,使用转换为某些整数类型的技巧是一个问题。因此,首先检查 x 是否在足够宽的整数范围内(精度超过 double 的整数)。 x 之外的值已经是整数。建议选择最宽的整数 (u)intmax_t

请记住,转换为整数是向 0 的舍入,而不是 floor。如果 x 是 negative/positive 而代码是 ceil()floor(),则需要不同的处理。 OP 的代码遗漏了这一点。

我会避免 if (x >= INTMAX_MAX) {,因为它涉及 (double) INTMAX_MAX,其舍入和精确值为 "chosen in an implementation-defined manner"。相反,我会与 INTMAX_MAX_P1 进行比较。 some_integer_MAX 是一个 Mersenne Number 和 2 的补码,...MIN 是一个取反的 "power of 2".

#include <inttypes.h>

#define INTMAX_MAX_P1 ((INTMAX_MAX/2 + 1)*2.0)

double my_ceil(double x) {
  if (x >= INTMAX_MAX_P1) {
    return x;
  }
  if (x < INTMAX_MIN) {
    return x;
  }

  intmax_t i = (intmax_t) x;      // this rounds towards 0
  if (i < 0 || x == i) return i;  // negative x is already rounded up.
  return i + 1.0;
}

由于 x 可能是 not-a-number,因此反转比较更有用,因为 NaN 的关系比较为假。

double my_ceil(double x) {
  if (x >= INTMAX_MIN && x < INTMAX_MAX_P1) {
    intmax_t i = (intmax_t) x;      // this rounds towards 0
    if (i < 0 || x == i) return i;  // negative x is already rounded up.
    return i + 1.0;
  }
  return x;
}

double my_floor(double x) {
  if (x >= INTMAX_MIN && x < INTMAX_MAX_P1) {
    intmax_t i = (intmax_t) x;      // this rounds towards 0
    if (i > 0 || x == i) return i;  // positive x is already rounded down.
    return i - 1.0;
  }
  return x;
}