关于浮点:在C ++中用于float的round()

round() for float in C++

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

1
2
3
4
5
double round(double);

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

我可以在数学h中找到ceil()floor(),但不能找到round()

它是否存在于另一个名称下的标准C++库中,还是丢失了??


在C++ 98标准库中没有循环()。不过你可以自己写一本。以下是Round Half-up的实现:

1
2
3
4
double round(double d)
{
  return floor(d + 0.5);
}

在C++ 98标准库中没有圆形函数的可能原因是它实际上可以以不同的方式实现。上面是一种常见的方法,但是还有其他一些方法,比如从整数到偶数,这就不那么偏颇了,如果要进行大量的四舍五入,通常会更好;不过,实现起来要复杂一些。


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

1
2
3
4
#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,EDOCX1为3,EDOCX1为4,EDCOX1为5。


C++ 03标准依赖于C90标准,该标准调用C++标准03标准(最接近公共可获得的草案标准到C++ 03是N1804)部分EDCOX1 0标准规范的标准C库。

The library described in clause 7 of ISO/IEC 9899:1990 and clause 7 of
ISO/IEC 9899/Amd.1:1995 is hereinafter called the Standard C
Library.1)

如果我们在C PrPoto上的C文档为圆、Ln圆、LLeNd,我们可以看到,圆和相关函数是C99的一部分,因此在C++ 03或之前是不可用的。

STD:STD:C++ 11中的这种变化,因为C++ 11依赖于C标准库的C99草稿标准,因此提供了STD::圆和积分返回类型STD::Lng:STD::LLNEX:

1
2
3
4
5
6
7
8
9
#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::

Computes nearest integer not greater in magnitude than arg.

1
2
3
4
5
6
7
8
9
10
#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应用程序,您最好使用升压圆、回调、L绕行、LLNEN或Boost Tunc。

滚动你自己的版本是困难的

滚动您自己的可能不值得这样做:舍入浮点到最近的整数,第1部分,舍入浮点到最近的整数,第2部分,舍入浮点到最近的整数,第3部分解释:

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

1
2
3
4
double myround(double d)
{
  return std::floor(d + 0.5);
}

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

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

A prvalue of a floating point type can be converted to a prvalue of an
integer type. The conversion truncates; that is, the fractional part
is discarded. The behavior is undefined if the truncated value cannot
be represented in the destination type.[...]

例如:

1
2
3
4
float myround(float f)
{
  return static_cast<float>( static_cast<unsigned int>( f ) ) ;
}

如果std::numeric_limits::max()4294967295,则以下调用:

1
myround( 4294967296.5f )

会导致溢出(现场观察)。

我们可以通过查看这个在C中实现round()的简洁方法的答案来了解这有多困难?它引用了新的单精度浮点循环的libs版本。对于一些看似简单的东西来说,它是一个非常长的函数。似乎任何不熟悉浮点实现的人都不可能正确地实现此功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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可能是一个选项,因为它是一个经过良好测试的实现。


可能值得注意的是,如果您希望从四舍五入中得到整数结果,则不需要通过CEIL或Floor。即。,

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


它可以从C+ 11在CMAISE中获得(根据HTTP//www-OpenSTD.Org/JTC1/SC22/WG21/DOCS/PoSs/2012/N337.PDF)

1
2
3
4
5
6
7
8
9
10
11
12
#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;
}

输出:

1
2
3
4
5
6
round(0.5):  1
round(-0.5): -1
round(1.4):  1
round(-1.4): -1
round(1.6):  2
round(-1.6): -2


它通常作为floor(value + 0.5)来实现。

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


我们正在研究两个问题:

  • 舍入转换
  • 类型转换。
  • 舍入转换是指舍入±float/double到最近的floor/ceil float/double。也许你的问题到此为止。但是,如果期望您返回int/long,则需要执行类型转换,因此"溢出"问题可能会影响您的解决方案。所以,检查函数中的错误

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #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;
    }

    请注意,只有在进行到整数的转换时,这才有效。


    您可以使用以下方法精确到n位:

    1
    2
    3
    4
    5
    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;
    }


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

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

    当以均匀随机值传递时,在我的机器上,这个值大约为8.88纳秒。

    就我所知,以下功能相当,但我的机器上的时钟为2.48纳秒,因此具有显著的性能优势:

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

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


    当心以东十一〔六〕号。以下是奇数在范围[2^52,2^53]中可能发生的情况:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    -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
    "
    ,x);
        printf("round(x)    =%f
    "
    ,y);
        printf("floor(x+0.5)=%f
    "
    ,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.squak.org/view.php?ID=7134。使用类似@konik的解决方案。

    我自己的健壮版本如下:

    1
    2
    3
    4
    5
    6
    7
    double round(double x)
    {
        double truncated,roundedFraction;
        double fraction = modf(x, &truncated);
        modf(2.0*fraction, &roundedFraction);
        return truncated + roundedFraction;
    }

    这里给出了避免地板(x+0.5)的另一个原因。


    现在,使用C++ 11编译器(包括C99/C++11数学库)应该不是问题。但问题是:你选择哪种取整函数?好的。

    C99/C++ 11 EDCOX1×0通常不是实际需要的舍入函数。它使用了一种时髦的舍入模式,将0舍入为中途情况下的平局(+-xxx.5000)。如果你确实想要舍入模式,或者你瞄准的是一个C++实现,其中EDCOX1×0比EDCOX1(3)那么快,那么就使用它(或者在这个问题上用它的另一个答案来模仿它的行为,使它面对面并仔细地再现特定的舍入行为)。好的。

    round()的舍入不同于ieee754默认舍入到最近的模式,甚至作为平局中断。最近的偶数避免了平均数量级的统计偏差,但对偶数有偏差。好的。

    有两个数学库舍入函数使用当前默认的舍入模式:EDCOX1 OR 5和EDCOX1 OR 6,两者都在C99/C+ + 11中添加,因此它们随时都可以使用EDCOX1 7。唯一的区别是,nearbyint从不增加fe-unceract。好的。

    出于性能方面的原因,倾向于使用rint():gcc和clang都更容易嵌入,但gcc从不使用nearbyint()(即使使用-ffast-math)。好的。用于x86-64和AARCH64的GCC/Clang

    我在MattGodbolt的编译器资源管理器上放置了一些测试函数,在这里您可以看到source+asm输出(对于多个编译器)。有关读取编译器输出的更多信息,请参阅本问答和Matt的CPPCon2017谈话:"我的编译器最近为我做了什么?打开编译器的盖子",好的。

    在fp代码中,内联小函数通常是一个巨大的胜利。特别是在非Windows上,标准调用约定没有保留调用的寄存器,因此编译器不能在一个call上的xmm寄存器中保留任何fp值。因此,即使您并不真正了解asm,您仍然可以很容易地看到它是否只是对库函数的一个尾部调用,或者它是否内联到一个或两个数学指令中。任何输入一个或两个指令的东西都比函数调用(对于x86或ARM上的这个特定任务)要好。好的。

    在x86上,任何与SSE4.1 roundsd相连接的东西都可以使用SSE4.1 roundpd或AVX vroundpd自动矢量化。(fp->integer转换也可采用压缩的simd格式,但fp->64位integer需要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的inline。
    • std::rint号:好的。

      • X86 Clang:Inlines to a single insn with EDOCX1
      • X86 GC7:Inlines to a single insn with EDOCX1(without sse4.1,inlines to several instructions)
      • X86 GC6.X and earlier:inlines to a single insn with EDOCX1
      • AARCH64 GCC:Inlines to a single insn by default
    • (P)字母名称好的,好的。
      • X86 CLANG:doesn't inline
      • X86 GCC:INLINES to multiple instructions with EDOCX1 original 2,requiring two vector constants.
      • Aarch64 GCC:Inlines to a single instruction(HW support for this rounding mode as well as IEEE default and most others.)
    • (P)字母名称好的,好的。
      • X86 Clang:Inlines to a single insn with EDOCX1
      • X86 GC7.X:Inlines to a single insn with EDOCX1
      • X86 GC6.X and earlier:inlines to a single insn with EDOCX1
      • AARCH64 GCC:Inlines by default to a single instruction

    1.Rounding to EDOCX1(音标11/EDOCX1)(P)You have two options here:use EDOCX1 commercial 14(like EDOCX1 penal 15 commercial but returns EDOCX1 commercial 12 commercial,or EDOCX1 pental 13 commercial for EDOCX1 pental 18 commercial),or use an FP->fp rounding function and then turn to an integer type the normal way(with truncation).有些参赛者的最佳选择是比其他人更好的好的,好的。字母名称(P)Note that EDOCX1 19 commercial converts EDOCX1/20 commercial or EDOCX1 original 21->EDOCX1/12 commercially 12,and then truncates the integer to EDOCX1 commercial 11.This make s a difference for out-of-range integers:undefined behaviour in C+,but well-defined for the X86 FP->int instructions(which the compiler will emin unless it sees the UB at compile time while doing constant promotion,then it's allowed to make code that breaks if it's every executed).好的,好的。(P)On X86,an FP->Integer conversion that overflows the integer produces EDOCX1 penographic 24 commonic or EDOCX1 commercial 25(a bit-pattern of EDOCX1 penographic 26 or the 64-bit equivalent,with just the sign-bit set).Intel calls this"integer indefinite"value.(See the EDOCX1 penography 27 communal manual entry,the SS2 instruction that converts(with truncation)scalar double to signed integer).It's available with 32-bit or 64-bit integer designation(in 64-bit mode only).There's also a EDOCX1 pental 28(convent with current rounding mode),which is what we'd like the compiler to emit,but unfortunately gcc and claiming won't do that without EDOCX1 penogical 29.好的,好的。(P)Also beware that FP to/From EDOCX1 penographic 30 original int/long is less efficient on X86(without avx512).Conversion to 32-bit unsigned on a 64-bit machine is pretty cheap;just transform to 64-bit signed and truncate.但是,如果它的意思是Slower。好的,好的。

    • (P)X86 Clang with/Without EDOCX1 Discussion 2:EDOCX1 Little 32 Lines to EDOCX1(Missed最优化to EDOCX1 English)EDOCX1字体14,全音不输入。好的,好的。
    • (P)X86 GC6.X and Earlier without EDOCX1 English 29:Neither way inlines好的,好的。
    • X86 GC7 Without EDOCX1 English 29:EDOCX1 penographic 32 communal rounds and converts separately(with 2 total instructions of sse4.1 is enabled,otherwise with a bunch of code inlined for EDOCX1 sil 15 without EDOCX1.字母名称:EDOCX1
    • (P)X86 GCC with EDOCX1 pension 29:all ways inline to EDOCX1 penal 28(optimal),no need for sse4.1.好的,好的。
    • aarch64 gcc6.3没有-ffast-math(int/long)rint内联到2的指示。lrint不直列.

      /好的。

    • aarch64 gcc6.3与-ffast-math(int/long)rintcompiles的呼叫的lrint。。。。。。。lrint不t的内联。这可能是一个错过的"双指令优化的除非我们找到没有-ffast-math是很慢的。

    好的。


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

    在C99

    对于类型泛型宏,我们有以下and和header

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

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

    1
    gcc -lm -std=c99 ...

    在C++ 11中

    我们在中有以下和其他重载,包括依赖于IEEE双精度浮点的

    1
    2
    3
    4
    5
    #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,也不产生警告和警告,X86Y64-W64-MIW32-G++6.3.0,CLAN-X86Y64 ++3.8,Visual C++ 2015社区。

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

    有序数除法

    当将两个序数(其中t是短的、int、长的或另一个序数)相除时,舍入表达式是这样的。

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

    准确度

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

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

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

    这不是舍入算法的问题。在选择类型和设计计算、数据输入和数字显示时,应考虑数学现实。如果应用程序显示显示这些十进制二进制转换问题的数字,那么应用程序将以可视方式表示数字现实中不存在且应该更改的精度。


    使用modf功能的double round(double)功能:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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轮


    基于Kalaxy的响应,下面是一个模板化的解决方案,它基于自然舍入将任何浮点数舍入为最接近的整数类型。如果值超出整数类型的范围,它还会在调试模式中引发错误,从而大致充当一个可行的库函数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
        // 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);
        }


    如果您需要能够在支持C++ 11标准的环境中编译代码,但还需要能够在不支持它的环境中编译相同的代码,那么可以使用函数宏在STD::()和()每个系统之间的自定义函数之间进行选择。只需将EDCOX1、7或EDCOX1、8的代码传递给符合C++ 11的编译器(或者使用其内置的版本宏),并生成这样的标题:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 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::()(包括保存符号位为-0)。但是,它可能会造成轻微的性能损失,并且可能会出现舍入某些已知"问题"浮点值(如0.499999999994或类似值)的问题。

    或者,如果你可以访问一个C++ 11兼容编译器,你可以从它的EDCOX1×9的头中抓取STD::Roud(),并使用它来定义自己的头,如果函数尚未定义,它将定义函数。注意,这可能不是一个最佳的解决方案,但是,特别是当您需要为多个平台编译时。


    正如注释和其他答案所指出的,ISO C++标准库没有添加EDCOX1〔0〕直到ISO C++11,当这个函数被引用到ISOC99标准数学库时。

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

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    #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
    "
    , argi, resi, refi);
                return EXIT_FAILURE;
            }
            argi++;
        } while (argi);
        return EXIT_SUCCESS;
    }


    我在X86架构中使用了Sun的ASM实现,以及MS与特定C++的实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    __forceinline int Round(const double v)
    {
        int r;
        __asm
        {
            FLD     v
            FISTP   r
            FWAIT
        };
        return r;
    }

    upd:返回双精度值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    __forceinline double dround(const double v)
    {
        double r;
        __asm
        {
            FLD     v
            FRNDINT
            FSTP    r
            FWAIT
        };
        return r;
    }

    输出:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // 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");

    这可能是一种低效的、肮脏的转换方式,但实际上,它是LOL工作的,而且很好,因为它适用于实际的浮点。不仅仅是视觉上影响输出。


    我做到了:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    #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;
        }
    }