【问题标题】:Overloaded comparison operator fails [C++11]重载比较运算符失败 [C++11]
【发布时间】:2013-06-26 12:55:26
【问题描述】:

我有一个带有两个重载比较运算符 (operator==) 的 RGB 颜色类。 一个用于 self 类型,一个用于 int (HEX)。

// this one assigns the value correctly
RGB     RGB::operator=(const int hex)
{
    this->r = (hex>>16 & 0xFF) / 255.0f;
    this->g = (hex>>8  & 0xFF) / 255.0f;
    this->b = (hex     & 0xFF) / 255.0f;
    return *this;
}
//--------------------------------------------------------------------------------------
// also works
bool    RGB::operator==(const RGB &color)
{
    return (r == color.r && g == color.g && b == color.b);
}
// this is evil
bool    RGB::operator==(const int hex)
{
    float rr = (hex>>16 & 0xFF) / 255.0f;
    float gg = (hex>>8  & 0xFF) / 255.0f;
    float bb = (hex     & 0xFF) / 255.0f;

    // if i uncomment these lines then everything is fine
    //std::cout<<r<<" "<<rr<<std::endl;
    //std::cout<<g<<" "<<gg<<std::endl;
    //std::cout<<b<<" "<<bb<<std::endl;

    return (r == rr &&
            g == gg &&
            b == bb);
}

RGB::RGB(int hex) 
{ 
  setHex(hex); 
} 

inline void RGB::setHex(unsigned hex) 
{ 
  r = (float)(hex >> 16 & 0xFF) / 255.0f; 
  g = (float)(hex >> 8 & 0xFF) / 255.0f; 
  b = (float)(hex & 0xFF) / 255.0f; 
}

...然后我在 main.cpp 中进行比较,例如:

RGB a = 0x555555;
bool equals = (a == 0x555555); // returns false

我不知道会发生什么。比较返回 false,但如果我取消注释定义中的 std::cout 行,则该函数按预期工作并返回 true。

这也没有问题:

RGB a = 0x555555;
RGB b = 0x555555;
bool equals = (a == b); // returns true

有人有想法吗?

【问题讨论】:

  • 为什么使用浮点作为 RGB 值的基础类型?
  • 因为它简化了颜色的计算(混合、颜色空间之间的转换等),并且 OpenGL 接受颜色分量作为浮点数。
  • @user2430597 请发布RGB::RGB( int hex )的定义。代码works 如果您只使用您发布的运算符。
  • RGB::RGB(int hex) { setHex(hex); } inline void RGB::setHex(unsigned hex) { r = (float)(hex >> 16 & 0xFF) / 255.0f; g = (float)(hex >> 8 & 0xFF) / 255.0f; b = (float)(hex & 0xFF) / 255.0f; }

标签: c++ c++11 comparison compare operator-overloading


【解决方案1】:

你不应该在没有优化的情况下获得浮点比较效果。这是因为你在这两种情况下都有相同的功能。

没有优化,以下是正确的:

float func(float);
float a = ...;
func(a) == func(a); //< always true

这就是你所拥有的,你的函数是移位并除以 255。

但是 - 有了优化,情况就不同了。 GCC 具有可以重新排列表达式的优化(参见例如 -freciprocal-math、-fassociative-math)。

在你的情况下,你有:

float rr = (X) / 255.0f;
...
r == rr

例如,它可以在优化时做一些相当于此的事情:

255.0f * r == (X)

现在确实受到浮点比较效应的影响。然而,通过在中间引入标准输出,你迫使它评估表达式更接近于它们的编写方式,这再次让你回到在同一个函数上针对自身评估真相的理智。

您可以更改类的定义以将值存储为整数并仅在需要浮点数时转换为浮点数,或者存储十六进制表示和浮点数并使用十六进制进行比较。或者您可以使用大于/小于而不是双等来比较测试两个浮点值是否在彼此的 1/255 范围内。例如。像这样:

return (abs(r - rr) < 1/255.0f && ...);

【讨论】:

    【解决方案2】:

    您是否知道您的 RGB::operator=() 永远不会被调用?

    RGB a = 0x555555;
    

    调用 RGB 的构造函数,该构造函数采用 int。 如果没有定义任何此类构造函数,您的代码将无法编译,因此给出的 sn-p 是不完整的。然而,

    RGB a;
    a = 0x555555;
    

    default 构造一个RGB 实例并调用您的RGB::operator=(int). I tried your code with both, clang++ and g++ and the comparison always evaluates totrue`。

    代码行为与std::cout 行注释或注释不同这一点非常奇怪。浮点比较可能是一些优化问题,这是邪恶的:google for "floating point comparison" 看看为什么。 为了验证这一点,我将附加一个调试器并查看rrrgggbbb 的实际(十六进制)值。

    请注意,您的赋值运算符应返回对*this 的引用而不是副本。

    【讨论】:

      【解决方案3】:

      谢谢大家,问题出在 JoshG79 所说的优化上。 为了解决这个问题,我首先尝试将计算存储在 volatile 变量中,这可能会阻止它们进行优化,但它们会做一些其他的事情并导致开销。所以我决定使用GCC function attributes

      所以头文件中的函数声明是这样的:

      bool operator==(const unsigned int hex) __attribute__((optimize("O0")));
      

      现在一切都很好,工作起来就像一个魅力。

      【讨论】:

      • 更健壮和便携的解决方案可能是将 (r==rr) 替换为 (abs(r-rr)
      • 这就是解决方案的关键:比较整数,而不是浮点数。但是,如何处理那些讨厌的、无声的隐式转换呢?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-07
      相关资源
      最近更新 更多