gpt4 book ai didi

c++ - 根据线性顺序使用最接近的值将数字裁剪为其他类型的数字限制

转载 作者:行者123 更新时间:2023-12-02 10:04:37 26 4
gpt4 key购买 nike

使用C++,我想将数字限制为可能小于源类型的另一种算术类型的数值范围。约束是,结果值应按整数集的线性顺序最接近源值。

  • 低于目标类型的最小限制的数字应转换为该最小限制
  • 大于目标类型最大限制的数字应转换为该最大限制
  • 应该适用于内置的整数和浮点类型(我没有看过第三方库中的多精度类型)

  • 例子:
  • 负数转换为无符号应返回0
  • 将未签名的65'535转换为int16应该返回32'767

  • 用例:库的使用者仅对所提供数字范围的一部分感兴趣。为了安全地处理超出目标范围的数字,转换需要匹配上述限制(因此,不可以使用强制转换)。

    实现(需要C++ 17)

    这是我想出的:
    #include <limits>

    template <typename T>
    constexpr T clip(T v, T lo, T hi)
    {
    return (v < lo) ? lo : (hi < v) ? hi : v;
    }

    template <typename TargetType, typename SourceType>
    constexpr TargetType clipToTargetTypeNumericLimits(SourceType v)
    {
    // This condition is necessary to not break integer -> floating point conversion
    // (the `static_cast<SourceType>(lo/hi)` below will give unwanted results),
    // while at the same time still clipping floating point type -> shorter floating point type.
    if constexpr (std::is_floating_point<TargetType>::value
    && !(
    std::is_floating_point<SourceType>::value
    && (std::numeric_limits<SourceType>::max_exponent
    >= std::numeric_limits<TargetType>::max_exponent)
    )
    )
    {
    return static_cast<TargetType>(v);
    }
    else
    {
    constexpr auto lo = std::numeric_limits<TargetType>::min();
    constexpr auto hi = std::numeric_limits<TargetType>::max();
    constexpr auto lo_sourceType = static_cast<SourceType>(lo);
    constexpr auto hi_sourceType = static_cast<SourceType>(hi);
    return static_cast<TargetType>(clip<SourceType>(v, lo_sourceType, hi_sourceType));
    // ... cannot use std::clamp, since it might assert when (lo_sourceType > hi_sourceType)
    // return static_cast<TargetType>(std::clamp(v, lo_sourceType, hi_sourceType));
    }
    }

    问题
  • 是否存在可以在boost或其他库中使用的实现(我看过,但是找不到一个)?
  • 而不是if constexpr条件,仅禁止对整数源类型和浮点目标类型的函数调用可能更干净。这也可能使其与C++ 11/14兼容。 std::enable_if如何用于检查源类型和目标类型?
  • C++ 20概念将使编译错误更具可读性。这个功能在概念上看起来如何? (应该删除if constexpr条件并替换为与之前的问题类似的概念吗?)
  • 您看到可能的意外行为吗?

  • 谢谢!

    测试
    #include <cassert>
    #include <iostream>

    template <typename SourceType, SourceType s,
    typename TargetType, TargetType expectedResult>
    void test_clipToTargetTypeNumericLimits()
    {
    constexpr auto result = clipToTargetTypeNumericLimits<TargetType>(s);
    std::cout << s << " -> " << +result << std::endl;
    static_assert(expectedResult == result);
    }

    template <typename SourceType, typename TargetType, typename S, typename T>
    void test_clipToTargetTypeNumericLimits(S f_s, T f_expectedResult)
    {
    constexpr auto s = f_s();
    constexpr auto expectedResult = f_expectedResult();
    constexpr auto result = clipToTargetTypeNumericLimits<TargetType>(s);
    std::cout << s << " -> " << +result << std::endl;
    static_assert(expectedResult == result);
    }

    int main()
    {
    std::cout << "\n--- negative signed integer -> unsigned ---" << std::endl;
    test_clipToTargetTypeNumericLimits<
    int32_t, -42, // source value
    uint32_t, 0>(); // expected result

    std::cout << "\n--- integer -> shorter integer type ---" << std::endl;
    test_clipToTargetTypeNumericLimits<
    uint32_t, UINT32_MAX, // source value
    uint16_t, UINT16_MAX>(); // expected result
    test_clipToTargetTypeNumericLimits<
    uint32_t, UINT32_MAX, // source value
    char, INT8_MAX>(); // expected result
    test_clipToTargetTypeNumericLimits<
    int64_t, INT32_MIN + 42, // source value
    int32_t, INT32_MIN + 42>(); // expected result

    std::cout << "\n--- floating point -> integer ---" << std::endl;
    test_clipToTargetTypeNumericLimits<
    float,
    unsigned>(
    [](){ return -42.7f; }, // source value
    [](){ return 0; }); // expected result
    test_clipToTargetTypeNumericLimits<
    double,
    uint8_t>(
    [](){ return 1024.5; }, // source value
    [](){ return UINT8_MAX; }); // expected result

    std::cout << "\n--- floating point -> shorter floating point type ---" << std::endl;
    test_clipToTargetTypeNumericLimits<
    double,
    float>(
    [](){ return std::numeric_limits<double>::min(); }, // source value
    [](){ return std::numeric_limits<float>::min(); }); // expected result

    test_clipToTargetTypeNumericLimits<
    long double,
    float>(
    [](){ return std::numeric_limits<long double>::max(); }, // source value
    [](){ return std::numeric_limits<float>::max(); }); // expected result

    std::cout << "\n--- integer -> floating point ---" << std::endl;
    test_clipToTargetTypeNumericLimits<
    uint64_t,
    float>(
    [](){ return UINT64_MAX; }, // source value
    [](){ return UINT64_MAX; }); // expected result

    std::cout << "\n--- to bool ---" << std::endl;
    constexpr auto b_f = clipToTargetTypeNumericLimits<bool>(0);
    std::cout << 0 << " -> " << std::boolalpha << b_f << std::endl;
    static_assert(0 == b_f);
    constexpr auto ldbl_max = std::numeric_limits<long double>::max();
    constexpr auto b_t = clipToTargetTypeNumericLimits<bool>(ldbl_max);
    std::cout << ldbl_max << " -> " << std::boolalpha << b_t << std::endl;
    static_assert(1 == b_t);

    std::cout << "\n--- evaluation at runtime ---" << std::endl;
    const auto duration_ticks = std::chrono::system_clock::now().time_since_epoch().count();
    const auto i8_at_runtime = clipToTargetTypeNumericLimits<int8_t>(duration_ticks);
    std::cout << duration_ticks << " -> " << +i8_at_runtime << std::endl;
    assert(INT8_MAX == i8_at_runtime);
    }

    可能的输出:

    --- negative signed integer -> unsigned ---
    -42 -> 0

    --- integer -> shorter integer type ---
    4294967295 -> 65535
    4294967295 -> 127
    -2147483606 -> -2147483606

    --- floating point -> integer ---
    -42.7 -> 0
    1024.5 -> 255

    --- floating point -> shorter floating point type ---
    2.22507e-308 -> 1.17549e-38
    1.18973e+4932 -> 3.40282e+38

    --- integer -> floating point ---
    18446744073709551615 -> 1.84467e+19

    --- to bool ---
    0 -> false
    1.18973e+4932 -> true

    --- evaluation at runtime ---
    1585315690266730 -> 127

    最佳答案

    我不太了解复杂的constexpr条件-注释似乎与任何现有代码都不匹配。

    我也没有非常仔细地检查逻辑。

    无论如何,我决定只解决由于使用constexpr-if而导致的C++ 17问题,而不是试图弄清楚您要做什么。

    有很多方法可以实现这一目标...我只展示三种可能性...

    在每种情况下,我只保留您写的条件。

    第一个带有enable if,但是如果您首先创建一个元函数则更容易阅读。

    namespace detail {
    template <typename TargetT, typename SourceT>
    struct IsFloatConversion
    : std::bool_constant<
    std::is_floating_point<TargetT>::value
    && !(std::is_floating_point<SourceT>::value
    && (std::numeric_limits<SourceT>::max_exponent
    >= std::numeric_limits<TargetT>::max_exponent))>
    {
    };
    }
    template <typename TargetType, typename SourceType>
    constexpr
    std::enable_if_t<detail::IsFloatConversion<TargetType, SourceType>::value,
    TargetType>
    clipToTargetTypeNumericLimits(SourceType v)
    {
    return static_cast<TargetType>(v);
    }
    template <typename TargetType, typename SourceType>
    constexpr
    std::enable_if_t<not detail::IsFloatConversion<TargetType, SourceType>::value,
    TargetType>
    clipToTargetTypeNumericLimits(SourceType v)
    {
    constexpr auto lo = std::numeric_limits<TargetType>::min();
    constexpr auto hi = std::numeric_limits<TargetType>::max();
    constexpr auto lo_sourceType = static_cast<SourceType>(lo);
    constexpr auto hi_sourceType = static_cast<SourceType>(hi);
    return static_cast<TargetType>(
    clip<SourceType>(v, lo_sourceType, hi_sourceType));
    }

    第二种方法只是标签分发。

    namespace detail {
    template <typename TargetType, typename SourceType>
    constexpr TargetType
    clipToTargetTypeNumericLimitsImpl(SourceType v, std::true_type)
    {
    return static_cast<TargetType>(v);
    }
    template <typename TargetType, typename SourceType>
    constexpr TargetType
    clipToTargetTypeNumericLimitsImpl(SourceType v, std::false_type)
    {
    constexpr auto lo = std::numeric_limits<TargetType>::min();
    constexpr auto hi = std::numeric_limits<TargetType>::max();
    constexpr auto lo_sourceType = static_cast<SourceType>(lo);
    constexpr auto hi_sourceType = static_cast<SourceType>(hi);
    return static_cast<TargetType>(
    clip<SourceType>(v, lo_sourceType, hi_sourceType));
    }
    }
    template <typename TargetType, typename SourceType>
    constexpr TargetType clipToTargetTypeNumericLimits(SourceType v)
    {
    constexpr bool dofloat = std::is_floating_point<TargetType>::value
    && !(
    std::is_floating_point<SourceType>::value
    && (std::numeric_limits<SourceType>::max_exponent
    >= std::numeric_limits<TargetType>::max_exponent)
    );
    return detail::clipToTargetTypeNumericLimitsImpl<TargetType>(v,
    std::integral_constant<bool, dofloat>{});
    }

    第三个是类似的,但是使用特化。

    namespace detail {
    template <bool>
    struct Clip
    {
    template <typename TargetType, typename SourceType>
    static constexpr TargetType _ (SourceType v)
    {
    return static_cast<TargetType>(v);
    }
    };
    template <>
    struct Clip<false>
    {
    template <typename TargetType, typename SourceType>
    static constexpr TargetType _ (SourceType v)
    {
    constexpr auto lo = std::numeric_limits<TargetType>::min();
    constexpr auto hi = std::numeric_limits<TargetType>::max();
    constexpr auto lo_sourceType = static_cast<SourceType>(lo);
    constexpr auto hi_sourceType = static_cast<SourceType>(hi);
    return static_cast<TargetType>(
    clip<SourceType>(v, lo_sourceType, hi_sourceType));
    }
    };
    }
    template <typename TargetType, typename SourceType>
    constexpr TargetType clipToTargetTypeNumericLimits(SourceType v)
    {
    constexpr bool dofloat = std::is_floating_point<TargetType>::value
    && !(
    std::is_floating_point<SourceType>::value
    && (std::numeric_limits<SourceType>::max_exponent
    >= std::numeric_limits<TargetType>::max_exponent)
    );
    return detail::Clip<dofloat>::template _<TargetType>(v);
    }

    还有其他方法,但是应该可以让您有足够的机会选择自己喜欢的东西-或打造自己更喜欢的东西。

    关于c++ - 根据线性顺序使用最接近的值将数字裁剪为其他类型的数字限制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60926694/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com