关系运算符基础知识
关系运算符允许你比较两个值。共有 6 个关系运算符:
| 运算符 | 符号 | 形式 | 操作 | 
|---|---|---|---|
| 大于 | > | x > y | 如果 x大于y则为true,否则为false | 
| 小于 | < | x < y | 如果 x小于y则为true,否则为false | 
| 大于等于 | >= | x >= y | 如果 x大于或等于y则为true,否则为false | 
| 小于等于 | <= | x <= y | 如果 x小于或等于y则为true,否则为false | 
| 等于 | == | x == y | 如果 x等于y则为true,否则为false | 
| 不等于 | != | x != y | 如果 x不等于y则为true,否则为false | 
这些运算符的工作方式大部分你已经见过,并且相当直观。每个运算符都会求值为布尔值 true (1) 或 false (0)。
关系运算符示例
以下是一个使用这些运算符与整数的示例代码:
#include <iostream>
int main()
{
    std::cout << "Enter an integer: ";
    int x{};
    std::cin >> x;
    std::cout << "Enter another integer: ";
    int y{};
    std::cin >> y;
    if (x == y)
        std::cout << x << " equals " << y << '\n';
    if (x != y)
        std::cout << x << " does not equal " << y << '\n';
    if (x > y)
        std::cout << x << " is greater than " << y << '\n';
    if (x < y)
        std::cout << x << " is less than " << y << '\n';
    if (x >= y)
        std::cout << x << " is greater than or equal to " << y << '\n';
    if (x <= y)
        std::cout << x << " is less than or equal to " << y << '\n';
    return 0;
}
运行结果示例:
Enter an integer: 4
Enter another integer: 5
4 does not equal 5
4 is less than 5
4 is less than or equal to 5
在比较整数时,这些运算符的使用极其简单直接。
布尔条件值使用指南
默认情况下,if 语句、条件运算符(以及其他一些地方)中的条件会求值为布尔值。
常见的布尔条件写法
许多新程序员会这样写语句:
if (b1 == true) ...
这是冗余的,因为 == true 实际上并没有为条件增加任何价值。相反,我们应该写成:
if (b1) ...
类似地,下面的写法:
if (b1 == false) ...
最好写成:
if (!b1) ...
最佳实践
避免在条件中添加不必要的
==或!=。这会使它们更难阅读,却不会提供任何额外价值。
浮点数比较的挑战与解决方案
浮点数比较的基本问题
考虑以下程序:
#include <iostream>
int main()
{
    constexpr double d1{ 100.0 - 99.99 }; // 数学上应该等于 0.01
    constexpr double d2{ 10.0 - 9.99 };   // 数学上应该等于 0.01
    if (d1 == d2)
        std::cout << "d1 == d2" << '\n';
    else if (d1 > d2)
        std::cout << "d1 > d2" << '\n';
    else if (d1 < d2)
        std::cout << "d1 < d2" << '\n';
    return 0;
}
浮点数比较的特殊情况
小于和大于比较
当小于 (<)、大于 (>)、小于等于 (<=) 和大于等于 (>=) 运算符用于浮点值时,在大多数情况下(当操作数的值不相似时)它们会产生可靠的结果。然而,如果操作数几乎相同,这些运算符应被视为不可靠。
等于和不等于比较
等于运算符 (== 和 !=) 则要麻烦得多。考虑以下示例:
#include <iostream>
int main()
{
    std::cout << std::boolalpha << (0.3 == 0.2 + 0.1); // 打印 false
    return 0;
}
浮点数比较的最佳实践
基本的近似相等比较
#include <cmath> // 为了 std::abs()
bool approximatelyEqualAbs(double a, double b, double absEpsilon)
{
    return std::abs(a - b) <= absEpsilon;
}
Knuth算法实现
#include <algorithm> // 为了 std::max
#include <cmath>     // 为了 std::abs
bool approximatelyEqualRel(double a, double b, double relEpsilon)
{
    return (std::abs(a - b) <= (std::max(std::abs(a), std::abs(b)) * relEpsilon));
}
综合解决方案
bool approximatelyEqualAbsRel(double a, double b, double absEpsilon, double relEpsilon)
{
    if (std::abs(a - b) <= absEpsilon)
        return true;
    return approximatelyEqualRel(a, b, relEpsilon);
}
C++23中的constexpr支持
现代C++实现
// C++23 版本
#include <algorithm>
#include <cmath>
constexpr bool approximatelyEqualRel(double a, double b, double relEpsilon)
{
    return (std::abs(a - b) <= (std::max(std::abs(a), std::abs(b)) * relEpsilon));
}
constexpr bool approximatelyEqualAbsRel(double a, double b, double absEpsilon, double relEpsilon)
{
    if (std::abs(a - b) <= absEpsilon)
        return true;
    return approximatelyEqualRel(a, b, relEpsilon);
}
C++14/17/20的替代方案
#include <algorithm>
#include <iostream>
template <typename T>
constexpr T constAbs(T x)
{
    return (x < 0 ? -x : x);
}
constexpr bool approximatelyEqualRel(double a, double b, double relEpsilon)
{
    return (constAbs(a - b) <= (std::max(constAbs(a), constAbs(b)) * relEpsilon));
}
constexpr bool approximatelyEqualAbsRel(double a, double b, double absEpsilon, double relEpsilon)
{
    if (constAbs(a - b) <= absEpsilon)
        return true;
    return approximatelyEqualRel(a, b, relEpsilon);
}
