如何正确比较整数和浮点值?

黑猫

如何以正确的方式比较整数和浮点值

内置比较运算符在某些情况下会给出错误的结果,例如:

#include <iomanip>
#include <iostream>

int main()
{
    long long a = 999999984306749439;
    float     b = 999999984306749440.f; // This number can be represented exactly by a `float`.

    std::cout << std::setprecision(1000);
    std::cout << a << " < " << b << " = " << (a < b) << '\n';
    // Prints `999999984306749439 < 999999984306749440 = 0`, but it should be `1`.
}

显然,比较运算符在实际比较它们之前将两个操作数都转换为相同类型。在这里,lhs转换为float,这会导致精度损失,并导致错误的结果。

即使我了解发生了什么,也不确定如何解决此问题。


免责声明:该示例使用floatlong long,但是我正在寻找一种通用解决方案,该解决方案适用于整数类型和浮点类型的每种组合。

黑猫

这就是我最后得到的。

该算法的功劳归@chux;他的方法似乎胜过其他建议。您可以在编辑历史记录中找到一些替代实现。

如果您认为有任何改进,欢迎提出建议。

#include <cmath>
#include <limits>
#include <type_traits>

enum partial_ordering {less, equal, greater, unordered};

template <typename I, typename F>
partial_ordering compare_int_float(I i, F f)
{
    if constexpr (std::is_integral_v<F> && std::is_floating_point_v<I>)
    {
        return compare_int_float(f, i);
    }
    else
    {
        static_assert(std::is_integral_v<I> && std::is_floating_point_v<F>);
        static_assert(std::numeric_limits<F>::radix == 2);

        // This should be exactly representable as F due to being a power of two.
        constexpr F I_min_as_F = std::numeric_limits<I>::min();

        // The `numeric_limits<I>::max()` itself might not be representable as F, so we use this instead.
        constexpr F I_max_as_F_plus_1 = F(std::numeric_limits<I>::max()/2+1) * 2;

        // Check if the constants above overflowed to infinity. Normally this shouldn't happen.
        constexpr bool limits_overflow = I_min_as_F * 2 == I_min_as_F || I_max_as_F_plus_1 * 2 == I_max_as_F_plus_1;
        if constexpr (limits_overflow)
        {
            // Manually check for special floating-point values.
            if (std::isinf(f))
                return f > 0 ? less : greater;
            if (std::isnan(f))
                return unordered;
        }

        if (limits_overflow || f >= I_min_as_F)
        {
            // `f <= I_max_as_F_plus_1 - 1` would be problematic due to rounding, so we use this instead.
            if (limits_overflow || f - I_max_as_F_plus_1 <= -1)
            {
                I f_trunc = f;
                if (f_trunc < i)
                    return greater;
                if (f_trunc > i)
                    return less;

                F f_frac = f - f_trunc;
                if (f_frac < 0)
                    return greater;
                if (f_frac > 0)
                    return less;

                return equal;
            }

            return less;
        }

        if (f < 0)
            return greater;

        return unordered;
    }
}

如果您想尝试一下,这里有一些测试案例:

#include <cmath>
#include <iomanip>
#include <iostream> 

void compare_print(long long a, float b, int n = 0)
{
    if (n == 0)
    {
        auto result = compare_int_float(a,b);
        std::cout << a << ' ' << "<=>?"[int(result)] << ' ' << b << '\n';
    }
    else
    {
        for (int i = 0; i < n; i++)
            b = std::nextafter(b, -INFINITY);

        for (int i = 0; i <= n*2; i++)
        {
            compare_print(a, b);
            b = std::nextafter(b, INFINITY);
        }

        std::cout << '\n';
    }
}

int main()
{    
    std::cout << std::setprecision(1000);

    compare_print(999999984306749440,
                  999999984306749440.f, 2);

    compare_print(999999984306749439,
                  999999984306749440.f, 2);

    compare_print(100,
                  100.f, 2);

    compare_print(-100,
                  -100.f, 2);

    compare_print(0,
                  0.f, 2);

    compare_print((long long)0x8000'0000'0000'0000,
                  (long long)0x8000'0000'0000'0000, 2);

    compare_print(42, INFINITY);
    compare_print(42, -INFINITY);
    compare_print(42, NAN);
    std::cout << '\n';

    compare_print(1388608,
                  1388608.f, 2);

    compare_print(12388608,
                  12388608.f, 2);
}

(运行代码)

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

如何比较浮点数和整数

来自分类Dev

为什么在scala中,将整数和浮点数(例如71 == 71.0)进行比较是正确的?

来自分类Dev

整数浮点数比较如何工作?

来自分类Dev

如何正确比较PHP中的字符串和整数?

来自分类Dev

Python和Z3:整数和浮点数,如何以正确的方式管理它们?

来自分类Dev

如何在mongodb中比较浮点值?

来自分类Dev

如何在对象中正确使用整数和浮点数?

来自分类Dev

熊猫数据框中的浮点和整数比较

来自分类Dev

如何从熊猫的对象列中提取和替换整数和浮点值

来自分类Dev

如何从熊猫的对象列中提取和替换整数和浮点值

来自分类Dev

如何比较实数和整数?

来自分类Dev

比较整数的正确方法

来自分类Dev

如何在Shell脚本中比较浮点值?

来自分类Dev

从NSString对象返回的浮点值不是我期望的值,如何正确从NSString中提取浮点值?

来自分类Dev

如何在C ++中正确规范化浮点值?

来自分类Dev

将浮点数比较为整数

来自分类Dev

如何分别专门针对整数和浮点类型的模板函数?

来自分类Dev

如何对列表中的整数和浮点数求和-Scala

来自分类Dev

如何找到字符串和浮点/整数的特定组合?

来自分类Dev

关于在C / C ++中比较整数和浮点型/双精度型

来自分类Dev

Java中的整数实例和int基本值比较

来自分类Dev

正确比较时间值

来自分类Dev

比较整数和原始

来自分类Dev

IBInspectable和浮点值

来自分类Dev

替代ceil()和floor()以获得最接近的整数值,在浮点值的上方和下方?

来自分类Dev

存在重复值时,如何比较数组中的整数索引?

来自分类Dev

将浮点数与数组中的值进行比较时,“ TypeError:只能将整数标量数组转换为标量索引”

来自分类Dev

将浮点数与数组中的值进行比较时,“ TypeError:只能将整数标量数组转换为标量索引”

来自分类Dev

相同的浮点值比较不相等

Related 相关文章

  1. 1

    如何比较浮点数和整数

  2. 2

    为什么在scala中,将整数和浮点数(例如71 == 71.0)进行比较是正确的?

  3. 3

    整数浮点数比较如何工作?

  4. 4

    如何正确比较PHP中的字符串和整数?

  5. 5

    Python和Z3:整数和浮点数,如何以正确的方式管理它们?

  6. 6

    如何在mongodb中比较浮点值?

  7. 7

    如何在对象中正确使用整数和浮点数?

  8. 8

    熊猫数据框中的浮点和整数比较

  9. 9

    如何从熊猫的对象列中提取和替换整数和浮点值

  10. 10

    如何从熊猫的对象列中提取和替换整数和浮点值

  11. 11

    如何比较实数和整数?

  12. 12

    比较整数的正确方法

  13. 13

    如何在Shell脚本中比较浮点值?

  14. 14

    从NSString对象返回的浮点值不是我期望的值,如何正确从NSString中提取浮点值?

  15. 15

    如何在C ++中正确规范化浮点值?

  16. 16

    将浮点数比较为整数

  17. 17

    如何分别专门针对整数和浮点类型的模板函数?

  18. 18

    如何对列表中的整数和浮点数求和-Scala

  19. 19

    如何找到字符串和浮点/整数的特定组合?

  20. 20

    关于在C / C ++中比较整数和浮点型/双精度型

  21. 21

    Java中的整数实例和int基本值比较

  22. 22

    正确比较时间值

  23. 23

    比较整数和原始

  24. 24

    IBInspectable和浮点值

  25. 25

    替代ceil()和floor()以获得最接近的整数值,在浮点值的上方和下方?

  26. 26

    存在重复值时,如何比较数组中的整数索引?

  27. 27

    将浮点数与数组中的值进行比较时,“ TypeError:只能将整数标量数组转换为标量索引”

  28. 28

    将浮点数与数组中的值进行比较时,“ TypeError:只能将整数标量数组转换为标量索引”

  29. 29

    相同的浮点值比较不相等

热门标签

归档