【问题标题】:Round a double to the closest and greater float将 double 舍入到最接近且更大的浮点数
【发布时间】:2013-03-08 12:30:13
【问题描述】:

我想使用 c/c++ 将大双精度数 (>1e6) 舍入到最接近但更大的浮点数。 我试过了,但我不确定它总是正确的,也许有最快的方法:

int main() {
    // x is the double we want to round
    double x = 100000000005.0;
    double y = log10(x) - 7.0;
    float a = pow(10.0, y);
    float b = (float)x;

    //c the closest round up float
    float c = a + b;
    printf("%.12f %.12f %.12f\n", c, b, x);
    return 0;
}

谢谢。

【问题讨论】:

  • 所以,1.2 应该变成2,对吧?
  • @jrok 你从哪里得到 OP 想要舍入为整数的想法?
  • 起初看起来很简单,但这确实是一个非常有趣的问题。愿意解释一下您是如何获得原始解决方案的吗?
  • 但你的意思是,最接近的较大整数(如整数的数学定义,即:整数)?或类似 1.234 -> 1.24
  • @MementoMori, float 的精度不如double,因此如果您可以在double 中表示100000000000000,则不能在float 中这样做.例如,float 中最接近的可能是 100000008765440。它根本不是传统意义上的四舍五入。

标签: c++ c floating-point rounding


【解决方案1】:

如果浮点数更大,只需将双精度分配给浮点数并返回即可。如果不是,则应该简单地将浮点数增加一个单位。 (对于正浮点数)。如果这仍然没有产生预期的结果,那么 double 大于 float 所支持的,在这种情况下,float 应该分配给 Inf。

float next(double a) {
    float b=a;
    if ((double)b > a) return b;
    return std::nextafter(b, std::numeric_limits<float>::infinity());
}

[Hack] next_after 的 C 版本(在选定的架构上)

float next_after(float a) {
    *(int*)&a += a < 0 ? -1 : 1;
    return a;
}

更好的方法是:

float next_after(float a) {
   union { float a; int b; } c = { .a = a };
   c.b += a < 0 ? -1 : 1;
   return c.a;
}

这两个自制的 hack 都忽略了 Infs 和 NaNs(并且仅适用于非负浮点数)。数学基于这样一个事实,即浮点数的二进制表示是有序的。要获得下一个可表示的浮点数,只需将二进制表示加一即可。

【讨论】:

  • 啊!我没有意识到nextafter 在 STL - +1 中!有关nextafter 的实现细节,realtimemadness.blogspot.com/2012/06/… 似乎是一个很好的资源
  • 应该简单地将浮点数增加一个单位。 (对于正浮点数)。如果这仍然没有产生预期的结果,那么双精度大于浮点支持的值。这不是真的。在 OP 的例子中,下一个 float 比 double 大 6000 左右。
  • @Shahbaz:在这种情况下,一个“单位”将超过 6000 - 它仍然是第一个大于或大于双精度的可表示浮点数
  • @Shahbaz:由于缺乏精度,一个浮点单位(在数字那么大的地方)将超过 6000。他说得对,它应该起作用。您还可以查看我在答案中发布的 jsfiddle,如果您需要证明,它会记录所有内容。
  • 不需要二次检验,也不需要特别对待无穷大。当b 是最大的有限浮点数时,std::nextafter(b, Inf) 返回无穷大,因为无穷大是最大有限值之后的下一个可表示值。 (假设Inf 是无穷大。我在C++ 标准中看不到Inf。)
【解决方案2】:

如果你使用,你可以使用nextafterf函数。

#include <stdio.h>
#include <math.h>
#include <float.h>

int main(){
  // x is the double we want to round
  double x=100000000005.0;

  float c = x;

  if ((double)c <= x)
    c = nextafterf(c, FLT_MAX);

  //c the closest round up float
  printf("%.12f %.12f\n",c,x);
  return 0;
}

【讨论】:

  • 不敢相信这个功能存在!
  • INFINITY 应在 nextafterf 表达式中使用,而不是 FLT_MAX。否则,当c 为FLT_MAX 时,此例程将无法返回c 之后的下一个可表示值(无穷大)。
  • Mankka, INFINITY 会让这个答案变得更好。
【解决方案3】:

C 有一个很好的 nextafter 函数,它会在这里有所帮助;

float toBiggerFloat( const double a ) {
    const float test = (float) a;
    return ((double) test < a) ? nextafterf( test, INFINITY ) : test;
}

这是一个测试脚本,它在所有数字类别(正/负、正常/次正常、无限、nan、-0)上显示它:http://codepad.org/BQ3aqbae(任何结果都可以正常工作)

【讨论】:

  • 在这方面,次常态的工作方式与其他值相同。 nextafterf 只返回下一个可表示的值。不需要循环。也不需要测试是否已达到无穷大。
  • @EricPostpischil:如果a 大于浮点数可以表示的值,那么它会在没有检查的情况下进入无限循环吗?或者你的意思是如果循环被删除?
  • 没有什么比 IEEE-754 float 可以表示的更大,因为 float 可以表示无穷大。但是,我的建议是消除循环。一次测试就足够了,因为nextafterf 的一个应用程序将保证新值超过a(如果a 为无穷大,则等于它)。
  • 考虑到“将双精度数舍入到最接近且 更大 浮点数”的目标,这个 toBiggerFloat(0) 返回 0。我希望值大于 0。跨度>
  • @chux-ReinstateMonica 因为 0 可以完美地表示为浮点数,这已经是您通常想要的(相当于上限)。但是对于您要问的,只需将条件中的&lt; 更改为&lt;=(相当于地板+ delta)。
猜你喜欢
  • 1970-01-01
  • 2022-09-27
  • 1970-01-01
  • 1970-01-01
  • 2013-06-13
  • 1970-01-01
  • 1970-01-01
  • 2018-07-17
  • 1970-01-01
相关资源
最近更新 更多