首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >两类ceil函数

两类ceil函数
EN

Code Review用户
提问于 2020-08-20 00:00:10
回答 1查看 448关注 0票数 10

ceil_constexpr是基于:https://stackoverflow.com/questions/8377412/ceil-function-how-can-we-implement-it-ourselves/8378022#8378022

ceil_constexpr2是一个利用截断的更简单的版本。

警告1:两个ceil函数都使用c++20。

警告2: ceil_constexpr使用bit_cast --我相信在目前的2020年8月20日只有MSVC v14.27支持的c++20函数。

代码语言:javascript
复制
#include <cstdint>
#include <concepts>
#include <limits>
#include <bit>
#include <exception>

template<typename T>
concept FloatingPoint =
    std::is_floating_point_v<T> &&
    (sizeof(T) == 4 || sizeof(T) == 8) &&//Only 32/64 bit allowed. 80 bit fp not allowed
    sizeof(float) == 4 && sizeof(double) == 8 &&//float must be 32 bit fp while double must be 64 bit fp
    std::numeric_limits<T>::is_iec559 == true &&// Only IEEE 754 fp allowed
    std::endian::native == std::endian::little;

template<FloatingPoint T>
constexpr bool isInf_constexpr(T inFp)//detect if infinity or -infinity
{
    constexpr bool is_T_Float = std::is_same_v<T, float>;
    using uintN_t = std::conditional_t<is_T_Float, uint32_t, uint64_t>;
    using intN_t = std::conditional_t<is_T_Float, int32_t, int64_t>;

    constexpr uintN_t mantissaBitNumber = is_T_Float ? 23 : 52;
    constexpr uintN_t infinityExponentValue = is_T_Float ? 0xff : 0x7ff; //the value of the exponent if infinity
    constexpr uintN_t positiveInfinityValue = infinityExponentValue << mantissaBitNumber;//the value of positive infinity
    constexpr uintN_t signRemovalMask = std::numeric_limits<intN_t>::max();//the max value of a signed int is all bits set to one except sign

    return ((std::bit_cast<uintN_t, T>(inFp) & signRemovalMask) == positiveInfinityValue);//remove sign before comparing against positive infinity value
}

template<FloatingPoint T>
constexpr bool isNaN_constexpr(T inFp)
{
    constexpr bool is_T_Float = std::is_same_v<T, float>;
    using uintN_t = std::conditional_t<is_T_Float, uint32_t, uint64_t>;
    using intN_t = std::conditional_t<is_T_Float, int32_t, int64_t>;

    constexpr uintN_t mantissaBitNumber = is_T_Float ? 23 : 52;
    constexpr uintN_t NaNExponentValue = is_T_Float ? 0xff : 0x7ff;//the value of the exponent if NaN
    constexpr uintN_t signRemovalMask = std::numeric_limits<intN_t>::max();//the max value of a signed int is all bits set to one except sign
    constexpr uintN_t exponentMask = NaNExponentValue << mantissaBitNumber;
    constexpr uintN_t mantissaMask = (~exponentMask) & signRemovalMask;//the bits of the mantissa are 1's, sign and exponent 0's.

    return ( 
        ((std::bit_cast<uintN_t, T>(inFp) & exponentMask) == exponentMask) &&//if exponent is all 1's
        ((std::bit_cast<uintN_t, T>(inFp) & mantissaMask) != 0) //if mantissa is != 0
        );

}

template<FloatingPoint T>
constexpr T ceil_constexpr(T inFp)
{
    if (isInf_constexpr<T>(inFp))
    {
        throw std::invalid_argument("Input floating point is infinity.");
    }
    else if (isNaN_constexpr<T>(inFp))
    {
        throw std::invalid_argument("Input floating point is NaN.");
    }

    constexpr bool is_T_Float = std::is_same_v<T, float>;
    
    constexpr uint32_t mantissaBitNumber = is_T_Float ? 23 : 52;
    constexpr uint32_t exponentMask = is_T_Float ? 255 : 2047;//used to remove the sign bit after the exponent bits
    constexpr uint32_t exponentBias = is_T_Float ? 127 : 1023;

    using uintN_t = std::conditional_t<is_T_Float, uint32_t, uint64_t>;
    using intN_t = std::conditional_t<is_T_Float, int32_t, int64_t>;


    const uintN_t input = std::bit_cast<uintN_t, T>(inFp);//bitwise copy floating point to unsigned integer

    const intN_t exponent = static_cast<intN_t>((input >> mantissaBitNumber) & exponentMask) - exponentBias;
    if (exponent < 0)
    {
        return (inFp > 0);
    }
    // small numbers get rounded to 0 or 1, depending on their sign

    const intN_t fractional_bits = static_cast<intN_t>(mantissaBitNumber) - exponent;
    if (fractional_bits <= 0)
    {
        return inFp;
    }
    // numbers without fractional bits are mapped to themselves

    
    //constexpr uintN_t uIntAllOnes = is_T_Float ? 0xffffffff : 0xffffffffffffffff;
    constexpr uintN_t uIntAllOnes = std::numeric_limits<uintN_t>::max();//store the max value of an unsigned integer (all bits are 1's)

    const uintN_t integral_mask = uIntAllOnes << fractional_bits;
    const uintN_t output = input & integral_mask;
    // round the number down by masking out the fractional bits


    inFp = std::bit_cast<T, uintN_t>(output);//bitwise copy unsigned integer to floating point

    if (inFp > 0 && output != input)
    {
        ++inFp;
    }
    // positive numbers need to be rounded up, not down


    return inFp;
}//algorithm from: https://stackoverflow.com/questions/8377412/ceil-function-how-can-we-implement-it-ourselves/8378022#8378022

template<FloatingPoint T>
constexpr T ceil_constexpr2(const T inFp)//simpler version
{
    if (isInf_constexpr<T>(inFp))
    {
        throw std::invalid_argument("Input floating point is infinity.");
    }
    else if (isNaN_constexpr<T>(inFp))
    {
        throw std::invalid_argument("Input floating point is NaN.");
    }

    constexpr bool is_T_Float = std::is_same_v<T, float>;

    using uintN_t = std::conditional_t<is_T_Float, uint32_t, uint64_t>;
    using intN_t = std::conditional_t<is_T_Float, int32_t, int64_t>;

    if (inFp > 0 && inFp != static_cast<intN_t>(inFp))
    {
        return static_cast<intN_t>(inFp + 1);
    }
    else
    {
        return static_cast<intN_t>(inFp);
    }
}
EN

回答 1

Code Review用户

发布于 2020-11-06 15:53:06

您错误地拼写了来自<cstdint>的类型名(它们都在std名称空间中;假设它们也在全局命名空间中是可移植性错误)。

虽然可能只是理论上的兴趣,但考虑到FloatingPoint概念所施加的条件,但是精确的宽度类型通常是一个糟糕的选择--更喜欢std::uint_fast64_t和其他宽度不会造成伤害的朋友。

我想对无穷大和NaNs抛出异常是一种有效的设计选择(我更希望返回的值不变,就像IEEE-754平台上的std::ceil()那样),但是这种行为应该在某个地方有明确的记录--可能是在头文件的开头。

票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/248169

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档