round()for C ++中的float

问题描述 投票:225回答:20

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

double round(double);

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

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

它是以另一个名称出现在标准C ++库中,还是缺少?

c++ floating-point rounding
20个回答
139
投票

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

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

C ++ 98标准库中没有循环函数的可能原因是它实际上可以以不同的方式实现。以上是一种常见的方式,但还有其他如round-to-even,如果你要进行大量的四舍五入,它会有较少的偏见并且通常会更好;但实施起来有点复杂。


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;
}

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


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;
}

避免楼层(x + 0.5)的另一个原因是here


3
投票

这些天使用包含C99 / C ++ 11数学库的C ++ 11编译器应该不是问题。但问题就变成了:你选择哪种舍入函数?

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

round()的舍入与IEEE754默认的round to nearest mode with even as a tie-break不同。最近 - 甚至避免了数字平均值的统计偏差,但是偏向偶数。

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

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


gcc/clang for x86-64 and AArch64

I put some test functions on Matt Godbolt's Compiler Explorer,你可以看到source + asm输出(对于多个编译器)。有关读取编译器输出的更多信息,请参阅this Q&A和Matt的CppCon2017谈话:“What Has My Compiler Done for Me Lately? Unbolting the Compiler's Lid”

在FP代码中,内联小函数通常是一个很大的胜利。特别是在非Windows上,标准调用约定没有调用保留的寄存器,因此编译器不能在call中保留XMM寄存器中的任何FP值。所以即使你真的不知道asm,你仍然可以很容易地看到它是否只是对库函数的尾调用,或者它是否内联到一个或两个数学指令。内联到一个或两个指令的任何内容都优于函数调用(对于x86或ARM上的此特定任务)。

在x86上,任何内联到SSE4.1 roundsd的东西都可以用SSE4.1 roundpd(或AVX vroundpd)自动矢量化。 (FP->整数转换也以压缩SIMD形式提供,但FP-> 64位整数除外,它需要AVX512。)

  • std::nearbyint(): x86 clang:使用-msse4.1内联到单个insn。 x86 gcc:仅使用-msse4.1 -ffast-math内联到单个insn,并且仅在gcc 5.4和更早版本上。后来gcc从未内联它(也许他们没有意识到其中一个直接位可以抑制不精确的异常?这就是clang使用的,但是当它内联时,较旧的gcc使用与rint相同的立即数) AArch64 gcc6.3:默认情况下内联到单个insn。
  • std::rint: x86 clang:使用-msse4.1内联到单个insn x86 gcc7:使用-msse4.1内联到单个insn。 (没有SSE4.1,内联几条指令) x86 gcc6.x及更早版本:使用-ffast-math -msse4.1内联到单个insn。 AArch64 gcc:默认情况下内联到单个insn
  • std::round: x86 clang:不内联 x86 gcc:使用-ffast-math -msse4.1内联多个指令,需要两个向量常量。 AArch64 gcc:内联到单个指令(硬件支持此舍入模式以及IEEE默认和大多数其他指令。)
  • std::floor / std::ceil / std::trunc x86 clang:使用-msse4.1内联到单个insn x86 gcc7.x:使用-msse4.1内联到单个insn x86 gcc6.x及更早版本:使用-ffast-math -msse4.1内联到单个insn AArch64 gcc:默认情况下内联到单个指令

Rounding to int / long / long long:

这里有两个选项:使用lrint(如rint但返回longlong long用于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->整数转换产生INT_MINLLONG_MIN0x8000000的位模式或64位等效,只有符号位设置)。英特尔将此称为“整数无限期”值。 (参见the cvttsd2si manual entry,SSE2指令将标量双精度转换(带截断)转换为有符号整数。它可用于32位或64位整数目的地(仅限64位模式)。还有一个cvtsd2si(使用当前舍入模式转换) ),这是我们希望编译器发出的,但不幸的是,如果没有-ffast-math,gcc和clang将不会这样做。

还要注意,来自unsigned int / long的FP在x86上效率较低(没有AVX512)。在64位计算机上转换为32位无符号非常便宜;只需转换为64位signed和truncate。但是否则它会明显变慢。

  • x86铿锵有/无-ffast-math -msse4.1(int/long)rint内联到roundsd / cvttsd2si。 (错过了对cvtsd2si的优化)。 lrint根本没有内联。
  • x86 gcc6.x和更早版没有-ffast-math:既没有内联方式
  • 没有-ffast-math的x86 gcc7:(int/long)rint单独转换和转换(启用SSE4.1的2个指令,否则使用rint内联的一堆代码而不使用roundsd)。 lrint没有内联。
  • x86 gcc与-ffast-math:所有方式内联到cvtsd2si(最佳),不需要SSE4.1。
  • 没有-ffast-math的AArch64 gcc6.3:(int/long)rint内联到2条指令。 lrint没有内联
  • 带有-ffast-math的AArch64 gcc6.3:(int/long)rint编写了对lrint的调用。 lrint没有内联。除非我们在没有-ffast-math的情况下获得的两条指令非常慢,否则这可能是错过的优化。

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);

还有equivalents in the std namespace

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

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

有序除法

当划分两个序数时,其中T是短的,int,long或另一个序数,舍入表达式就是这个。

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

准确性

毫无疑问,在浮点运算中出现奇怪的不准确性,但这仅在数字出现时才会出现,并且与舍入无关。

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

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

这不是舍入算法的问题。在选择类型以及计算,数据输入和数字显示的设计时应考虑数学现实。如果应用程序显示显示这些十进制二进制转换问题的数字,那么应用程序将直观地表达数字现实中不存在的准确性,应该进行更改。


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”和“限制”是必要的。该函数根据以下舍入模式工作:

  • 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

1
投票

如果您需要能够在支持C ++ 11标准的环境中编译代码,但还需要能够在不支持它的环境中编译相同的代码,您可以使用函数宏在std之间进行选择:: round()和每个系统的自定义函数。只需将-DCPP11/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(),并使用它来创建自己的头,定义函数(如果尚未定义)。请注意,这可能不是最佳解决方案,尤其是在需要针对多个平台进行编译时。


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);
    }

1
投票

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

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

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

我使用英特尔编译器版本13,使用my_roundf()roundf(),详细测试了/fp:strict对newlib /fp:fast的实现。我还检查了newlib版本与英特尔编译器的roundf()库中的mathimf匹配。双精度round()不可能进行详尽的测试,但代码在结构上与单精度实现完全相同。

#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;
}

0
投票

我在as86 for x86架构和MS VS特定C ++中使用了下面的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

-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");

它可能是一种低效的肮脏转换方式,但是,它工作大声笑。它很好,因为它适用于实际浮动。不只是视觉上影响输出。


94
投票

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 documentation

编辑:从C ++ 11开始,有std::round, std::lround, and std::llround


-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;
    }
}

77
投票

C ++ 03标准依赖于C90标准,该标准称为标准C库,C ++ 03标准草案(closest publicly available draft standard to C++03 is N1804)部分涵盖了1.2规范性引用:

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

如果我们去C documentation for round, lround, llround on cppreference,我们可以看到圆形和相关函数是C99的一部分,因此在C ++ 03或之前不可用。

在C ++ 11中,这种情况发生了变化,因为C ++ 11依赖于C标准库的C99草案标准,因此提供了std::round and for integral return types 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

滚动你自己的版本很难

滚动你自己可能不值得努力,因为Harder than it looks: rounding float to nearest integer, part 1Rounding float to nearest integer, part 2Rounding float to nearest integer, part 3解释:

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

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

这将失败的一个输入是0.49999999999999994,(see it live)。

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

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

例如:

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

鉴于std::numeric_limits<unsigned int>::max()4294967295,接下来的电话:

myround( 4294967296.5f ) 

会引起溢出,(see it live)。

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

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可能是一个选项,因为它是一个经过良好测试的实现。


71
投票

值得注意的是,如果你想要一个舍入的整数结果,你不需要通过ceil或floor。即,

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

41
投票

自c ++中的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

27
投票

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

编辑:它可能不会被调用,因为我知道至少有三种舍入算法:舍入到零,舍入到最接近的整数,以及银行家的舍入。你要求舍入到最接近的整数。


13
投票

我们正在考虑两个问题:

  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


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;
}

请注意,这仅适用于进行整数转换。


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;
}
© www.soinside.com 2019 - 2024. All rights reserved.