245

我需要一个简单的浮点舍入函数,因此:

double round(double);

round(0.1) = 0
round(-0.1) = 0
round(-0.9) = -1

我可以在 math.h中找到ceil()and - 但不是。floor()round()

它是否以另一个名称出现在标准 C++ 库中,还是丢失了?

4

23 回答 23

145

编者注:以下答案提供了一个简单的解决方案,其中包含几个实现缺陷(有关完整解释,请参阅Shafik Yaghmour 的答案)。请注意,C++11 已经包含std::roundstd::lroundstd::llroundas 内置函数。

C++98 标准库中没有 round()。不过你可以自己写一个。以下是round-half-up的实现:

double round(double d)
{
  return floor(d + 0.5);
}

C++98 标准库中没有 round 函数的可能原因是它实际上可以以不同的方式实现。以上是一种常见的方法,但还有其他方法,例如round-to-even,如果您要进行大量舍入,则偏差较小且通常更好;不过实现起来有点复杂。

于 2009-01-27T22:10:28.577 回答
96

Boost 提供了一组简单的舍入函数。

#include <boost/math/special_functions/round.hpp>

double a = boost::math::round(1.5); // Yields 2.0
int b = boost::math::iround(1.5); // Yields 2 as an integer

有关详细信息,请参阅Boost 文档

编辑:从 C++11 开始,有std::round,std::lroundstd::llround.

于 2011-05-01T16:19:30.537 回答
94

C++03 标准依赖于 C90 标准,该标准称为标准 C 库,该标准包含在 C++03 标准草案(与 C++03 最接近的公开可用草案标准是 N1804)部分1.2 规范性参考

ISO/IEC 9899:1990 第 7 条和 ISO/IEC 9899/Amd.1:1995 第 7 条中描述的库在下文中称为标准 C 库。1)

如果我们在 cppreference 上查看 round、lround、llround 的 C 文档,我们可以看到round和相关函数是C99的一部分,因此在 C++03 或更早版本中不可用。

在 C++11 中,这发生了变化,因为 C++11 依赖于C 标准库的 C99 草案标准,因此提供std::round 和整数返回类型 std::lround、std::llround

#include <iostream>
#include <cmath>

int main()
{
    std::cout << std::round( 0.4 ) << " " << std::lround( 0.4 ) << " " << std::llround( 0.4 ) << std::endl ;
    std::cout << std::round( 0.5 ) << " " << std::lround( 0.5 ) << " " << std::llround( 0.5 ) << std::endl ;
    std::cout << std::round( 0.6 ) << " " << std::lround( 0.6 ) << " " << std::llround( 0.6 ) << std::endl ;
}

C99 的另一个选项是std::trunc,它:

计算幅度不大于 arg 的最接近整数。

#include <iostream>
#include <cmath>

int main()
{
    std::cout << std::trunc( 0.4 ) << std::endl ;
    std::cout << std::trunc( 0.9 ) << std::endl ;
    std::cout << std::trunc( 1.1 ) << std::endl ;
    
}

如果您需要支持非 C++11 应用程序,最好的办法是使用boost round、iround、lround、llroundboost trunc

滚动你自己的圆形版本很难

自己滚动可能不值得付出努力,因为比看起来更难:将浮点数舍入到最接近的整数,第 1 部分,将浮点数舍入到最接近的整数,第 2 部分将浮点数舍入到最接近的整数,第 3 部分解释:

例如,您的实现使用std::floor和添加的常见滚动0.5不适用于所有输入:

double myround(double d)
{
  return std::floor(d + 0.5);
}

这将失败的一个输入是0.49999999999999994,(现场查看)。

另一种常见的实现涉及将浮点类型转换为整数类型,这可以在整数部分无法在目标类型中表示的情况下调用未定义的行为。我们可以从草稿 C++ 标准部分4.9 Floating-integral Conversions中看到这一点(强调我的):

浮点类型的纯右值可以转换为整数类型的纯右值。转换截断;也就是说,小数部分被丢弃。如果截断的值不能在目标类型中表示,则行为未定义。[...]

例如:

float myround(float f)
{
  return static_cast<float>( static_cast<unsigned int>( f ) ) ;
}

给定std::numeric_limits<unsigned int>::max()的是4294967295以下调用:

myround( 4294967296.5f ) 

会导致溢出,(现场观看)。

通过查看Concise way to implement round() in C的答案,我们可以看到这到底有多困难?它引用了单精度浮点数的newlibs版本。对于看似简单的事情来说,这是一个很长的函数。对浮点实现没有深入了解的人似乎不太可能正确实现此功能:

float roundf(x)
{
  int signbit;
  __uint32_t w;
  /* Most significant word, least significant word. */
  int exponent_less_127;

  GET_FLOAT_WORD(w, x);

  /* Extract sign bit. */
  signbit = w & 0x80000000;

  /* Extract exponent field. */
  exponent_less_127 = (int)((w & 0x7f800000) >> 23) - 127;

  if (exponent_less_127 < 23)
    {
      if (exponent_less_127 < 0)
        {
          w &= 0x80000000;
          if (exponent_less_127 == -1)
            /* Result is +1.0 or -1.0. */
            w |= ((__uint32_t)127 << 23);
        }
      else
        {
          unsigned int exponent_mask = 0x007fffff >> exponent_less_127;
          if ((w & exponent_mask) == 0)
            /* x has an integral value. */
            return x;

          w += 0x00400000 >> exponent_less_127;
          w &= ~exponent_mask;
        }
    }
  else
    {
      if (exponent_less_127 == 128)
        /* x is NaN or infinite. */
        return x + x;
      else
        return x;
    }
  SET_FLOAT_WORD(x, w);
  return x;
}

另一方面,如果其他解决方案都不可用,那么newlib可能是一种选择,因为它是一个经过良好测试的实现。

于 2014-06-22T04:07:51.643 回答
71

值得注意的是,如果您想从舍入中获得整数结果,则无需将其传递给 ceil 或 floor。IE,

int round_int( double r ) {
    return (r > 0.0) ? (r + 0.5) : (r - 0.5); 
}
于 2011-01-11T16:45:51.687 回答
50

它从 cmath 中的 C++11 开始可用(根据http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf

#include <cmath>
#include <iostream>

int main(int argc, char** argv) {
  std::cout << "round(0.5):\t" << round(0.5) << std::endl;
  std::cout << "round(-0.5):\t" << round(-0.5) << std::endl;
  std::cout << "round(1.4):\t" << round(1.4) << std::endl;
  std::cout << "round(-1.4):\t" << round(-1.4) << std::endl;
  std::cout << "round(1.6):\t" << round(1.6) << std::endl;
  std::cout << "round(-1.6):\t" << round(-1.6) << std::endl;
  return 0;
}

输出:

round(0.5):  1
round(-0.5): -1
round(1.4):  1
round(-1.4): -1
round(1.6):  2
round(-1.6): -2
于 2012-06-19T13:23:09.353 回答
28

它通常实现为floor(value + 0.5).

编辑:它可能不叫round,因为我知道至少有三种舍入算法:四舍五入到零,四舍五入到最接近的整数,以及银行家的四舍五入。您要求四舍五入到最接近的整数。

于 2009-01-27T22:08:38.097 回答
14

我们正在研究两个问题:

  1. 舍入转换
  2. 类型转换。

舍入转换意味着舍入 ± float/double 到最接近的 floor/ceil float/double。可能你的问题到此为止。但是如果您希望返回 Int/Long,则需要执行类型转换,因此“溢出”问题可能会影响您的解决方案。所以,检查你的函数中的错误

long round(double x) {
   assert(x >= LONG_MIN-0.5);
   assert(x <= LONG_MAX+0.5);
   if (x >= 0)
      return (long) (x+0.5);
   return (long) (x-0.5);
}

#define round(x) ((x) < LONG_MIN-0.5 || (x) > LONG_MAX+0.5 ?\
      error() : ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))

来自:http ://www.cs.tut.fi/~jkorpela/round.html

于 2010-05-29T08:49:02.577 回答
11

Boost 中还实现了某种类型的舍入:

#include <iostream>

#include <boost/numeric/conversion/converter.hpp>

template<typename T, typename S> T round2(const S& x) {
  typedef boost::numeric::conversion_traits<T, S> Traits;
  typedef boost::numeric::def_overflow_handler OverflowHandler;
  typedef boost::numeric::RoundEven<typename Traits::source_type> Rounder;
  typedef boost::numeric::converter<T, S, Traits, OverflowHandler, Rounder> Converter;
  return Converter::convert(x);
}

int main() {
  std::cout << round2<int, double>(0.1) << ' ' << round2<int, double>(-0.1) << ' ' << round2<int, double>(-0.9) << std::endl;
}

请注意,这仅在您进行整数转换时才有效。

于 2010-06-29T13:02:15.750 回答
7

如今,使用包含 C99/C++11 数学库的 C++11 编译器应该不是问题。但是问题就变成了:您选择哪个舍入函数?

C99/C++11round()实际上通常不是您想要的舍入函数。它使用一种时髦的舍入模式,从 0 舍入作为中途情况下的抢七(+-xxx.5000)。如果您确实特别想要这种舍入模式,或者您的目标round()是比 更快的 C++ 实现rint(),那么请使用它(或使用该问题的其他答案之一来模拟其行为,该答案从表面上看并仔细复制了该特定舍入行为。)

round()的舍入不同于 IEEE754 默认舍入到最接近的模式,甚至作为 tie-break。Nearest-even避免了数字平均大小的统计偏差,但确实偏向偶数。

有两个使用当前默认舍入模式的数学库舍入函数:std::nearbyint()std::rint(),都在 C99/C++11 中添加,因此它们随时可用std::round()。唯一的区别是它nearbyint永远不会引发 FE_INEXACT。

rint()出于性能原因更喜欢:gcc 和 clang 都更容易内联,但 gcc 从不内联nearbyint()(即使使用-ffast-math


x86-64 和 AArch64 的 gcc/clang

在 Matt Godbolt 的 Compiler Explorer 上放置了一些测试函数,您可以在其中看到 source + asm 输出(用于多个编译器)。有关阅读编译器输出的更多信息,请参阅此问答和 Matt 的 CppCon2017 演讲:“我的编译器最近为我做了什么?解开编译器的盖子”</a>,

在 FP 代码中,内联小函数通常是一个巨大的胜利。特别是在非 Windows 上,标准调用约定没有保留调用的寄存器,因此编译器无法在call. 因此,即使您并不真正了解 asm,您仍然可以轻松查看它是否只是对库函数的尾调用,还是内联到一两条数学指令。任何内联到一个或两个指令的东西都比函数调用好(对于 x86 或 ARM 上的这个特定任务)。

在 x86 上,内联到 SSE4.1 的任何内容都roundsdroundpd可以使用 SSE4.1 (或 AVX )自动矢量化vroundpd。(FP->integer 转换也可以打包 SIMD 形式使用,除了需要 AVX512 的 FP->64 位整数。)

  • std::nearbyint()

    • x86 clang:内联到单个 insn 与-msse4.1.
    • x86 gcc:内联到单个 insn 仅与-msse4.1 -ffast-math,并且仅在 gcc 5.4 和更早版本上。后来 gcc 从来没有内联它(也许他们没有意识到立即位之一可以抑制不精确的异常?这就是 clang 使用的,但是旧的 gcc 使用与rint内联时相同的立即数)
    • AArch64 gcc6.3:默认内联到单个insn。
  • std::rint

    • x86 clang:内联到单个insn-msse4.1
    • x86 gcc7:内联到带有-msse4.1. (没有SSE4.1,内联到几条指令)
    • x86 gcc6.x 及更早版本:内联到带有-ffast-math -msse4.1.
    • AArch64 gcc:默认内联到单个 insn
  • std::round

    • x86 clang:不内联
    • x86 gcc:内联到多个指令-ffast-math -msse4.1,需要两个向量常量。
    • AArch64 gcc:内联到单个指令(硬件支持这种舍入模式以及 IEEE 默认和大多数其他模式。)
  • std::floor// std::ceil_std::trunc

    • x86 clang:内联到单个insn-msse4.1
    • x86 gcc7.x:内联到单个insn-msse4.1
    • x86 gcc6.x 及更早版本:内联到单个insn-ffast-math -msse4.1
    • AArch64 gcc:默认内联到单个指令

四舍五入到int/ long/ long long

您在这里有两个选择:使用lrint(如rint但返回long,或long longfor llrint),或使用 FP->FP 舍入函数,然后以正常方式(使用截断)转换为整数类型。一些编译器优化一种方式比另一种更好。

long l = lrint(x);

int  i = (int)rint(x);

请注意,首先int i = lrint(x)转换floatdouble-> long,然后将整数截断为int. 这对超出范围的整数产生了影响:C++ 中的未定义行为,但为 x86 FP -> int 指令定义良好(编译器将发出,除非它在编译时看到 UB,同时进行常量传播,然后它是允许制作如果曾经执行过就会中断的代码)。

在 x86 上,溢出整数的 FP->integer 转换产生INT_MINor LLONG_MIN0x8000000或 64 位等效的位模式,仅设置符号位)。英特尔将此称为“整数不定”值。(请参阅手册cvttsd2si条目,将(截断)标量双精度数转换为有符号整数的 SSE2 指令。它适用于 32 位或 64 位整数目标(仅在 64 位模式下)。还有一个cvtsd2si(使用当前舍入转换mode),这是我们希望编译器发出的,但不幸的是 gcc 和 clang 不会在没有-ffast-math.

另请注意,unsigned在 x86(没有 AVX512)上,往返 int / long 的 FP 效率较低。在 64 位机器上转换为 32 位无符号是相当便宜的;只需转换为 64 位有符号并截断即可。但除此之外,它的速度要慢得多。

  • x86 clang with/without -ffast-math -msse4.1: (int/long)rintinlines to roundsd/ cvttsd2si。(错过了对 的优化cvtsd2si)。 lrint根本不内联。

  • x86 gcc6.x 及更早版本没有-ffast-math: 都不是内联

  • x86 gcc7 without -ffast-math:分别舍入和转换(启用了 SSE4.1 的总共 2 条指令,否则为without(int/long)rint内联一堆代码)。 不内联。rintroundsdlrint
  • x86 gcc with -ffast-math : allways inline to cvtsd2si(optimal),不需要 SSE4.1。

  • AArch64 gcc6.3 没有-ffast-math:(int/long)rint内联到 2 条指令。 lrint不内联

  • -ffast-math带有:的AArch64 gcc6.3(int/long)rint编译为对lrint. lrint不内联。这可能是一个错过的优化,除非我们得到的两条指令-ffast-math非常慢。
于 2017-11-17T09:27:33.497 回答
6

您可以使用以下方法四舍五入到 n 位精度:

double round( double x )
{
const double sd = 1000; //for accuracy to 3 decimal places
return int(x*sd + (x<0? -0.5 : 0.5))/sd;
}
于 2010-02-12T03:02:23.563 回答
5

如果您最终想将函数的double输出转换为,那么这个问题的公认解决方案将类似于:round()int

int roundint(double r) {
  return (int)((r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5));
}

当传入统一的随机值时,这在我的机器上的时钟大约为8.88 ns 。

据我所知,以下在功能上是等效的,但在我的机器上的时钟频率为2.48 ns,具有显着的性能优势:

int roundint (double r) {
  int tmp = static_cast<int> (r);
  tmp += (r-tmp>=.5) - (r-tmp<=-.5);
  return tmp;
}

性能更好的原因之一是跳过的分支。

于 2013-04-25T21:02:09.957 回答
4

当心floor(x+0.5)。以下是 [2^52,2^53] 范围内的奇数可能发生的情况:

-bash-3.2$ cat >test-round.c <<END

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

int main() {
    double x=5000000000000001.0;
    double y=round(x);
    double z=floor(x+0.5);
    printf("      x     =%f\n",x);
    printf("round(x)    =%f\n",y);
    printf("floor(x+0.5)=%f\n",z);
    return 0;
}
END

-bash-3.2$ gcc test-round.c
-bash-3.2$ ./a.out
      x     =5000000000000001.000000
round(x)    =5000000000000001.000000
floor(x+0.5)=5000000000000002.000000

这是http://bugs.squeak.org/view.php?id=7134。使用类似@konik 的解决方案。

我自己的强大版本将类似于:

double round(double x)
{
    double truncated,roundedFraction;
    double fraction = modf(x, &truncated);
    modf(2.0*fraction, &roundedFraction);
    return truncated + roundedFraction;
}

此处给出了避免使用 floor(x+0.5) 的另一个原因。

于 2012-06-17T20:50:40.727 回答
2

函数double round(double)配合使用modf函数:

double round(double x)
{
    using namespace std;

    if ((numeric_limits<double>::max() - 0.5) <= x)
        return numeric_limits<double>::max();

    if ((-1*std::numeric_limits<double>::max() + 0.5) > x)
        return (-1*std::numeric_limits<double>::max());

    double intpart;
    double fractpart = modf(x, &intpart);

    if (fractpart >= 0.5)
        return (intpart + 1);
    else if (fractpart >= -0.5)
        return intpart;
    else
        return (intpart - 1);
    }

要编译干净,包括“math.h”和“limits”是必要的。该函数根据以下舍入模式工作:

  • 5.0 轮是 5.0
  • 3.8 轮是 4.0
  • 2.3轮是2.0
  • 1.5轮是2.0
  • 0.501 的回合是 1.0
  • 0.5 轮是 1.0
  • 0.499 的回合是 0.0
  • 0.01 的回合是 0.0
  • 0.0 的回合是 0.0
  • -0.01 的回合是 -0.0
  • -0.499 的回合是 -0.0
  • -0.5 的回合是 -0.0
  • -0.501 的回合是 -1.0
  • -1.5 的回合是 -1.0
  • -2.3 的回合是 -2.0
  • -3.8 的回合是 -4.0
  • -5.0 的回合是 -5.0
于 2011-02-09T11:35:25.343 回答
2

不需要实现任何东西,所以我不确定为什么这么多答案涉及定义、函数或方法。

在 C99 中

对于泛型宏,我们有以下和头文件 <tgmath.h>。

#include <math.h>
double round (double x);
float roundf (float x);
long double roundl (long double x);

如果你不能编译它,你可能遗漏了数学库。与此类似的命令适用于我拥有的每个 C 编译器(几个)。

gcc -lm -std=c99 ...

在 C++11 中

我们在 #include <cmath> 中有以下和其他重载,它们依赖于 IEEE 双精度浮点。

#include <math.h>
double round (double x);
float round (float x);
long double round (long double x);
double round (T x);

std 命名空间中也有等价物。

如果你不能编译它,你可能正在使用 C 编译而不是 C++。对于 g++ 6.3.1、x86_64-w64-mingw32-g++ 6.3.0、clang-x86_64++ 3.8.0 和 Visual C++ 2015 Community,以下基本命令既不会产生错误也不会产生警告。

g++ -std=c++11 -Wall

带序数除法

当划分两个序数时,其中 T 是 short、int、long 或其他序数,舍入表达式是 this。

T roundedQuotient = (2 * integerNumerator + 1)
    / (2 * integerDenominator);

准确性

毫无疑问,浮点运算中会出现看起来很奇怪的不准确性,但这只是在数字出现时,与四舍五入无关。

来源不仅仅是浮点数的 IEEE 表示的尾数中的有效位数,它与我们人类的十进制思维有关。

10 是 5 和 2 的乘积,5 和 2 互质。因此,IEEE 浮点标准不可能完美地表示为所有二进制数字表示的十进制数。

这不是舍入算法的问题。在选择类型和设计计算、数据输入和数字显示时,应考虑数学现实。如果应用程序显示显示这些十进制-二进制转换问题的数字,则该应用程序在视觉上表达了数字现实中不存在的准确性,应该进行更改。

于 2017-02-04T08:16:47.193 回答
1

根据 Kalaxy 的回复,以下是一个模板化的解决方案,它基于自然舍入将任何浮点数四舍五入为最接近的整数类型。如果值超出整数类型的范围,它也会在调试模式下引发错误,从而大致用作可行的库函数。

    // round a floating point number to the nearest integer
    template <typename Arg>
    int Round(Arg arg)
    {
#ifndef NDEBUG
        // check that the argument can be rounded given the return type:
        if (
            (Arg)std::numeric_limits<int>::max() < arg + (Arg) 0.5) ||
            (Arg)std::numeric_limits<int>::lowest() > arg - (Arg) 0.5)
            )
        {
            throw std::overflow_error("out of bounds");
        }
#endif

        return (arg > (Arg) 0.0) ? (int)(r + (Arg) 0.5) : (int)(r - (Arg) 0.5);
    }
于 2013-10-24T00:03:47.680 回答
1

如果您需要能够在支持 C++11 标准的环境中编译代码,但还需要能够在不支持它的环境中编译相同的代码,您可以使用函数宏在 std 之间进行选择::round() 和每个系统的自定义函数。只需将-DCPP11or传递/DCPP11给符合 C++11 的编译器(或使用其内置版本宏),并制作如下标题:

// File: rounding.h
#include <cmath>

#ifdef CPP11
    #define ROUND(x) std::round(x)
#else    /* CPP11 */
    inline double myRound(double x) {
        return (x >= 0.0 ? std::floor(x + 0.5) : std::ceil(x - 0.5));
    }

    #define ROUND(x) myRound(x)
#endif   /* CPP11 */

有关简单示例,请参阅http://ideone.com/zal709

这近似于不符合 C++11 的环境中的 std::round(),包括为 -0.0 保留符号位。但是,它可能会导致轻微的性能下降,并且可能会在舍入某些已知的“问题”浮点值时出现问题,例如 0.49999999999999994 或类似值。

或者,如果您可以访问符合 C++11 的编译器,则可以从其<cmath>标头中获取 std::round() ,并使用它来制作您自己的标头来定义该函数(如果尚未定义)。但是请注意,这可能不是最佳解决方案,尤其是当您需要针对多个平台进行编译时。

于 2015-12-16T00:59:22.983 回答
1

正如评论和其他答案中所指出的,ISO C++ 标准库round()直到 ISO C++11 才添加,当时这个函数是通过引用 ISO C99 标准数学库引入的。

对于 [½, ub ]round(x) == floor (x + 0.5)中的正操作数,当映射到 IEEE-754 (2008) 时ub为 2 23float当映射到 IEEE-754 (2008) 时ub 为binary322 52。数字 23 和 52 对应于这两种浮点格式中存储的尾数位数。对于 [+0, ½)中的正操作数和 ( ub , +∞]中的正操作数。由于函数关于 x 轴对称,因此可以根据 处理负参数。doublebinary64round(x) == 0round(x) == xxround(-x) == -round(x)

这导致下面的紧凑代码。它可以跨各种平台编译成合理数量的机器指令。我观察到 GPU 上最紧凑的代码,其中my_roundf()需要大约十几个指令。根据处理器架构和工具链,这种基于浮点的方法可能比其他答案中引用的 newlib 中基于整数的实现更快或更慢。

我使用英特尔编译器版本 13my_roundf()对 newlib 实现进行了详尽的测试,同时使用和. 我还检查了 newlib 版本是否与Intel 编译器库中的版本匹配。双精度无法进行详尽的测试,但是代码在结构上与单精度实现相同。roundf()/fp:strict/fp:fastroundf()mathimfround()

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>

float my_roundf (float x)
{
    const float half = 0.5f;
    const float one = 2 * half;
    const float lbound = half;
    const float ubound = 1L << 23;
    float a, f, r, s, t;
    s = (x < 0) ? (-one) : one;
    a = x * s;
    t = (a < lbound) ? x : s;
    f = (a < lbound) ? 0 : floorf (a + half);
    r = (a > ubound) ? x : (t * f);
    return r;
}

double my_round (double x)
{
    const double half = 0.5;
    const double one = 2 * half;
    const double lbound = half;
    const double ubound = 1ULL << 52;
    double a, f, r, s, t;
    s = (x < 0) ? (-one) : one;
    a = x * s;
    t = (a < lbound) ? x : s;
    f = (a < lbound) ? 0 : floor (a + half);
    r = (a > ubound) ? x : (t * f);
    return r;
}

uint32_t float_as_uint (float a)
{
    uint32_t r;
    memcpy (&r, &a, sizeof(r));
    return r;
}

float uint_as_float (uint32_t a)
{
    float r;
    memcpy (&r, &a, sizeof(r));
    return r;
}

float newlib_roundf (float x)
{
    uint32_t w;
    int exponent_less_127;

    w = float_as_uint(x);
    /* Extract exponent field. */
    exponent_less_127 = (int)((w & 0x7f800000) >> 23) - 127;
    if (exponent_less_127 < 23) {
        if (exponent_less_127 < 0) {
            /* Extract sign bit. */
            w &= 0x80000000;
            if (exponent_less_127 == -1) {
                /* Result is +1.0 or -1.0. */
                w |= ((uint32_t)127 << 23);
            }
        } else {
            uint32_t exponent_mask = 0x007fffff >> exponent_less_127;
            if ((w & exponent_mask) == 0) {
                /* x has an integral value. */
                return x;
            }
            w += 0x00400000 >> exponent_less_127;
            w &= ~exponent_mask;
        }
    } else {
        if (exponent_less_127 == 128) {
            /* x is NaN or infinite so raise FE_INVALID by adding */
            return x + x;
        } else {
            return x;
        }
    }
    x = uint_as_float (w);
    return x;
}

int main (void)
{
    uint32_t argi, resi, refi;
    float arg, res, ref;

    argi = 0;
    do {
        arg = uint_as_float (argi);
        ref = newlib_roundf (arg);
        res = my_roundf (arg);
        resi = float_as_uint (res);
        refi = float_as_uint (ref);
        if (resi != refi) { // check for identical bit pattern
            printf ("!!!! arg=%08x  res=%08x  ref=%08x\n", argi, resi, refi);
            return EXIT_FAILURE;
        }
        argi++;
    } while (argi);
    return EXIT_SUCCESS;
}
于 2017-11-16T11:57:30.687 回答
0

对于 x86 体系结构和 MS VS 特定的 C++,我在 asm 中使用以下 round 实现:

__forceinline int Round(const double v)
{
    int r;
    __asm
    {
        FLD     v
        FISTP   r
        FWAIT
    };
    return r;
}

UPD:返回双精度值

__forceinline double dround(const double v)
{
    double r;
    __asm
    {
        FLD     v
        FRNDINT
        FSTP    r
        FWAIT
    };
    return r;
}

输出:

dround(0.1): 0.000000000000000
dround(-0.1): -0.000000000000000
dround(0.9): 1.000000000000000
dround(-0.9): -1.000000000000000
dround(1.1): 1.000000000000000
dround(-1.1): -1.000000000000000
dround(0.49999999999999994): 0.000000000000000
dround(-0.49999999999999994): -0.000000000000000
dround(0.5): 0.000000000000000
dround(-0.5): -0.000000000000000
于 2014-10-28T10:32:50.327 回答
0

round_f 用于带有数学的 ARM

static inline float round_f(float value)
{
    float rep;
    asm volatile ("vrinta.f32 %0,%1" : "=t"(rep) : "t"(value));
    return rep;
}

round_f 用于没有数学的 ARM

union f__raw {
    struct {
        uint32_t massa  :23;
        uint32_t order  :8;
        uint32_t sign   :1;
    };
    int32_t     i_raw;
    float       f_raw;
};

float round_f(float value)
{
    union f__raw raw;
    int32_t exx;
    uint32_t ex_mask;
    raw.f_raw = value;
    exx = raw.order - 126;
    if (exx < 0) {
        raw.i_raw &= 0x80000000;
    } else if (exx < 24) {
        ex_mask = 0x00ffffff >> exx;
        raw.i_raw += 0x00800000 >> exx;
        if (exx == 0) ex_mask >>= 1;
        raw.i_raw &= ~ex_mask;
    };
    return  raw.f_raw;
};
于 2022-01-13T00:21:14.173 回答
-1

将浮点值四舍五入“n”小数位的最佳方法是在 O(1) 时间内如下:-

我们必须将值四舍五入 3 位,即 n=3。所以,

float a=47.8732355;
printf("%.3f",a);
于 2019-08-09T01:34:44.397 回答
-1

从 C++ 11 开始:

#include <cmath>
std::round(1.1)

或得到 int

static_cast<int>(std::round(1.1))
于 2020-07-19T20:28:17.510 回答
-4
// Convert the float to a string
// We might use stringstream, but it looks like it truncates the float to only
//5 decimal points (maybe that's what you want anyway =P)

float MyFloat = 5.11133333311111333;
float NewConvertedFloat = 0.0;
string FirstString = " ";
string SecondString = " ";
stringstream ss (stringstream::in | stringstream::out);
ss << MyFloat;
FirstString = ss.str();

// Take out how ever many decimal places you want
// (this is a string it includes the point)
SecondString = FirstString.substr(0,5);
//whatever precision decimal place you want

// Convert it back to a float
stringstream(SecondString) >> NewConvertedFloat;
cout << NewConvertedFloat;
system("pause");

这可能是一种低效的肮脏转换方式,但见鬼,它有效,哈哈。这很好,因为它适用于实际的浮动。不仅在视觉上影响输出。

于 2010-06-29T12:31:34.290 回答
-6

我这样做了:

#include <cmath.h>

using namespace std;

double roundh(double number, int place){

    /* place = decimal point. Putting in 0 will make it round to whole
                              number. putting in 1 will round to the
                              tenths digit.
    */

    number *= 10^place;
    int istack = (int)floor(number);
    int out = number-istack;
    if (out < 0.5){
        floor(number);
        number /= 10^place;
        return number;
    }
    if (out > 0.4) {
        ceil(number);
        number /= 10^place;
        return number;
    }
}
于 2009-05-09T16:47:36.557 回答