Is there a trunc function in C++?

后端 未结 7 815
情歌与酒
情歌与酒 2021-01-19 04:52

I searched around and couldn\'t find the trunc function for C++. I know I can do this:

int main()
{
    double a = 12.566789;
    cout <<          


        
相关标签:
7条回答
  • 2021-01-19 05:40

    I've developed a very fast trunc-function:

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

    It's faster than most implemementations. On my Ryzen 7 1800X the average execution-time of values >= 2^0 and <= 2^54 is 12 clock cycles.

    0 讨论(0)
提交回复
热议问题