C++中是否有截断函数?

4

我搜索了一圈,但找不到 C++ 的 trunc 函数。我知道可以这样做:

int main()
{
    double a = 12.566789;
    cout << setprecision(2) << fixed << (int)(a * 100) / 100.0 << endl;
    return 0;
}

但我不确定这是否是最佳方法。谢谢。


1
这是一种方法。trunc() 是另一种方法。你正在寻找一个截断(trunc)函数,而不是一个大的脚储物柜(trunk)函数。 - David Hammen
7个回答

22
如果您的C库版本过旧,没有提供C99规范中指定的trunc函数,则可以基于C89规范中指定的floorceil函数轻松实现一个。
double trunc(double d){ return (d>0) ? floor(d) : ceil(d) ; }

5

trunc 是在 <cmath> 中的:

#include <iostream>
#include <cmath>

int main() {
        std::cout << trunc(3.141516) << std::endl; 
}

我想你在寻找其他内容吧?

7
这在VS2010中不可用! - Alireza Noori
1
这并不是。对比 POSIX、C90、C99 标准与 Microsoft Studio 实现的 math.h:http://www.johndcook.com/math_h.html。 - David Hammen

3

C语言中有一个截断函数,你也可以在C++中使用它。

trunc(a*100)/100

请记住,您仍然需要指定格式请求,因为浮点数无法精确表示所有实数,如果您不告诉输出代码您想要的精度,您可能会得到如 12.560000000112.55999999 的输出。

简而言之:

使用以下内容进行输出:

cout << setprecision(2) << fixed << a<< endl;

如果在数学计算过程中需要截断结果,则可以使用以下方法:

trunc(a*100)/100

(最好使用定点数学。)

1
在使用 double 时,您需要使用 trunc 而不是 truncl(后者适用于 long double)。 - Stephen Canon
1
@Stephen:你说得对。我没有仔细看手册,而是假设第一个版本适用于“int”,尽管这对于“trunc”没有任何意义。 - Ken Bloom
1
在VS2010中,它显示:“错误C3861:未找到标识符'trunc'”。这是一个标准函数吗?在提问之前我已经测试了trunc。还有一件事,setprecision(2)会四舍五入值,而不是截断。 - Alireza Noori
2
@Alireza:trunc在C99标准中被添加,因此在某些不带现代C库的旧版C++编译器中不存在。 C++11最终将其添加到C标准中,因此希望那些供应商在未来几年内终于现代化其数学库。 - Stephen Canon
1
那么与此同时,我必须用我在这里发布的代码创建自己的版本。对吗? - Alireza Noori
显示剩余2条评论

1
当然可以。使用 math.h 中的 trunc() 函数。它是一个 C 函数,但在 C++ 中也能像在 C 中一样工作。如果您想保留几个数字,您可以这样做:
double a = 12.566789;
double b = trunc(a * 100) / 100.0;

1

如果你正在使用一个没有实现trunc的古老的C或C++库,可以使用boost::math::trunc


1
好的,它不是古老的。我使用VS2010。 - Alireza Noori

0

我开发了一个非常快速的截断函数:

double ftrunc( double d )
{
    static_assert(sizeof(double) == sizeof(uint64_t), "sizeof(double) not equal to sizeof(uint64_t)");
    static_assert(numeric_limits<double>::is_iec559,  "double must be IEEE-754");
    // assume size_t is our CPU's native register-width
    static_assert(sizeof(size_t) == sizeof(uint64_t) || sizeof(size_t) == sizeof(uint32_t), "register-width must be 32 or 64 bit");
    if constexpr( sizeof(size_t) == sizeof(uint64_t) )
    // we have 64 bit registers
    {
        unsigned const MANTISSA_BITS           = 52,
                       EXP_BIAS                = 0x3FF,
                       INF_NAN_BASE            = 0x7FF;
        uint64_t const EXP_MASK                = (uint64_t)0x7FF                      << MANTISSA_BITS,
                       SIGN_MASK               = (uint64_t)0x800                      << MANTISSA_BITS ,
                       MIN_INTEGRAL_DIGITS_EXP = (uint64_t) EXP_BIAS                  << MANTISSA_BITS,
                       MIN_INTEGRAL_ONLY_EXP   = (uint64_t)(EXP_BIAS + MANTISSA_BITS) << MANTISSA_BITS,
                       INF_NAN_EXP             = (uint64_t)INF_NAN_BASE               << MANTISSA_BITS,
                       NEG_MANTISSA_MASK       = 0x000FFFFFFFFFFFFFu;
        union
        {
            double   du;
            uint64_t dx;
        };
        du = d;
        uint64_t exp = dx & EXP_MASK;
        if( exp >= MIN_INTEGRAL_DIGITS_EXP )
            // value has integral digits
            if( exp < MIN_INTEGRAL_ONLY_EXP )
            {
                // there are fraction-digits to mask out, mask them
                unsigned shift  = (unsigned)(exp >> MANTISSA_BITS) - EXP_BIAS;
                dx             &= ~(NEG_MANTISSA_MASK >> shift);
                return du;
            }
            else
                if( exp < INF_NAN_EXP )
                    // value is integral
                    return du;
                else
                    // infinite, NaN, SNaN
                    // raise exception on SNaN if necessary
                    return du + du;
        else
        {
            // below  +/-1.0
            // return +/-0.0
            dx &= SIGN_MASK;
            return du;
        }
    }
    else if constexpr( sizeof(size_t) == sizeof(uint32_t) )
    // we have 32 bit registers
    {
        unsigned const MANTISSA_BITS           = 52,
                       HI_MANTISSA_BITS        = 20,
                       EXP_BIAS                = 0x3FF,
                       INF_NAN_BASE            = 0x7FF;
        uint32_t const EXP_MASK                = (uint32_t)0x7FFu                        << HI_MANTISSA_BITS,
                       SIGN_MASK               = (uint32_t)0x800u                        << HI_MANTISSA_BITS,
                       MIN_INTEGRAL_DIGITS_EXP = (uint32_t) EXP_BIAS                     << HI_MANTISSA_BITS,
                       MAX_INTEGRAL32_EXP      = (uint32_t)(EXP_BIAS + HI_MANTISSA_BITS) << HI_MANTISSA_BITS,
                       MIN_INTEGRAL_ONLY_EXP   = (uint32_t)(EXP_BIAS + MANTISSA_BITS)    << HI_MANTISSA_BITS,
                       INF_NAN_EXP             = (uint32_t)INF_NAN_BASE                  << HI_MANTISSA_BITS,
                       NEG_HI_MANTISSA_MASK    = 0x000FFFFFu,
                       NEG_LO_MANTISSA_MASK    = 0xFFFFFFFFu;
        union
        {
            double du;
            struct
            {
                uint32_t dxLo;
                uint32_t dxHi;
            };
        };
        du = d;
        uint32_t exp = dxHi & EXP_MASK;
        if( exp >= MIN_INTEGRAL_DIGITS_EXP )
            // value has integral digits
            if( exp < MIN_INTEGRAL_ONLY_EXP )
                // there are fraction-digits to mask out
                if( exp <= MAX_INTEGRAL32_EXP )
                {
                    // the fraction digits are in the upper dword, mask them and zero the lower dword
                    unsigned shift  = (unsigned)(exp >> HI_MANTISSA_BITS) - EXP_BIAS;
                    dxHi           &= ~(NEG_HI_MANTISSA_MASK >> shift);
                    dxLo            = 0;
                    return du;
                }
                else
                {
                    // the fraction digits are in the lower dword, mask them
                    unsigned shift  = (unsigned)(exp >> HI_MANTISSA_BITS) - EXP_BIAS - HI_MANTISSA_BITS;
                    dxLo           &= ~(NEG_LO_MANTISSA_MASK >> shift);
                    return du;
                }
            else
                if( exp < INF_NAN_EXP )
                    // value is integral
                    return du;
                else
                    // infinite, NaN, SNaN
                    // raise exception on SNaN if necessary
                    return du + du;
        else
        {
            // below  +/-1.0
            // return +/-0.0
            dxHi &= SIGN_MASK;
            dxLo  = 0;
            return du;
        }
    }
}

它比大多数实现都要快。在我的Ryzen 7 1800X上,值>= 2^0且<= 2^54的平均执行时间为12个时钟周期。


-1

使用cmath中的ceilfloor


trunc() 正好符合 OP 的需求,而 ceil 和 floor 不行。 - David Hammen
1
如果他的C库太旧,缺少“trunc”函数,那么他可以轻松实现“double trunc(double d){ return (d>0) ? floor(d) : ceil(d) ; }”。 - Ken Bloom

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接