【问题标题】:Why can I compare sbyte to all the other numeric types *except* ulong?为什么我可以将 sbyte 与所有其他数字类型*除了* ulong 进行比较?
【发布时间】:2011-05-18 16:19:31
【问题描述】:

您可以在 sbyte 和 byte、int、uint、short、ushort、long、double 和 float 之间进行 >、

我的大脑正在爆炸。谁能解释为什么 sbyte 可以与 uint 进行比较,但 不是 ulong?

public bool sbyte_ulong_compare(sbyte x, ulong y)
{
    return x < y;  // compiler error CS0019
}

此外,使用unchecked 并不会让事情变得更好。大脑融化。

另一个编辑。这有效:

public bool sbyte_ulong_compare(sbyte x, ulong y)
{   
    //
    // returns x < y
    //
    if (x < 0)
        return true;

    if (y > 127)
        return true;

    return ((long)x < (long)y);
}

【问题讨论】:

  • 好问题!但是,对于我们这些没有尝试过的人,当你尝试比较时会发生什么?您以什么方式不能比较这些类型(抛出异常、无法编译等...)?
  • @djacobson:编译器吐出Operator '==' cannot be applied to operands of type 'sbyte' and 'ulong' 至少它在 .NET 3.5 项目中的 VS2k8 中是这样。
  • 我加了一个短代码sn-p。
  • 回复:unchecked - 假设 this 是您所指的关键字,它会抑制溢出检查,与无效的布尔比较无关。
  • @djacobson:是的。我想,嗯,也许魔法会发生。 :(

标签: c# .net clr


【解决方案1】:

dthorpe 和 Jon 的答案很接近,但并不完全正确。

正确的推理如下。

规范规定:

对于 x op y 形式的运算, 其中 op 是比较运算符, 重载决议是 应用于选择特定的运算符 实施。

好的,重载解析必须使用哪些运算符实现?它们是:

bool operator <(int x, int y);
bool operator <(uint x, uint y);
bool operator <(long x, long y);
bool operator <(ulong x, ulong y);
bool operator <(float x, float y);
bool operator <(double x, double y);
bool operator <(decimal x, decimal y);

加上所有枚举类型的枚举小于运算符,以及上述每个类型的提升为可空的版本。

重载解决首先要消除不适用的算子,然后从剩下的一组适用的算子中,确定最佳算子。

int、uint、long 和 enum 运算符(以及它们的提升形式)都被消除了,因为 ulong 不会隐式转换为这些类型。

uint 和 ulong 运算符(以及它们的提升形式)都被消除了,因为 sbyte 不会隐式转换为这些类型。

剩下的

bool operator <(float x, float y);
bool operator <(double x, double y);
bool operator <(decimal x, decimal y);

以及它们的提升形式。我们现在必须从这六个中确定 best 运算符。

我们所说的“最佳”是什么意思?在比较两个运算符时,具有 更具体 操作数类型的运算符更好。 “更具体”是指“老虎”比“动物”更具体,因为所有老虎都可以转换为动物,但并非所有动物都可以转换为老虎。

显然,未提升的形式比所有相应的提升形式都要好。不可为空的类型比其对应的可空类型更具体,因为不可为空的类型总是可以转换为它的可空类型,但反之则不行。我们可以消除提升的形式。

剩下三个。这三个哪个最好?

float 比 double 更具体。每个 float 都可以转换为 double,但并非每个 double 都可以转换为 float。因此消除了双倍。剩下两个。

bool operator <(float x, float y);
bool operator <(decimal x, decimal y);

其中哪一个是最好的?没有从浮点数到十进制数的隐式转换。没有从十进制到浮点的隐式转换。因此,谁都不比谁好。

因此无法确定最佳运算符。重载解析失败。

我们决定报告一个通用错误消息,它只是说没有这样的运算符可以做你想要的,而不是给出看似奇怪和令人困惑的错误消息“运算符重载解析失败,因为浮点数既不比也不比十进制”。我认为这是一个合理的设计选择。

【讨论】:

  • 这个答案太棒了。谢谢!
  • 我们有decimal 让这个失败了。 ulonglong 被转换为 float 进行比较的想法是可怕的,给出明显不合适的结果。我想如果我们这里没有decimal,我们真的必须有其他规则来防止这种情况发生。
  • 最后一步看起来不对。转换是从 sbyte/ulong 到 float 或 decimal。不在浮点数和小数之间。它失败了,因为两者都不是“更好”。比较 CS0121。也不需要不确定的双重消除。
  • @Hans:最后一步是对的。继续:我们有两个运算符:o1:浮点比较,o2:十进制比较。我们有四种转换:C11:sbyte to float,C12:ulong to float,C21:sbyte to decimal,C22:ulong to decimal。要使 o1 优于 o2,则 C11 必须优于 C21,或者 C21 必须优于 C22。 C11比C21好吗?为了使 C11 优于 C21,必须是浮点数比十进制更具体的情况。浮点数比十进制更具体吗?那么,浮点数是否转换为十进制但十进制不转换为浮点数?不,C11 并不比 C21 好。
  • 按类似的逻辑,C21不比C11好,C12不比C22好,C22也不比C21好。由于没有一个转换比另一个更好,因此无法确定最佳运算符。相关问题是“哪种目的地类型更具体?”并且通过考虑目标类型之间的转换来回答这个问题。 根据目标类型彼此之间的可转换性,从源类型到目标类型的一次转换优于另一次。
【解决方案2】:

当你比较两个不同整数类型的整数时,运算的类型是可以表示两个操作数组合的完整范围的最小整数类型。如果将有符号字节与 uint 进行比较,则运算类型为 long,因为 long 有足够的范围覆盖有符号字节的负数部分和 uint 的正数部分。

当您尝试比较 sbyte 和 ulong 时,没有整数类型可以跨越 ulong 和有符号字节的负部分的范围。编译器只考虑内置整数类型。不包括隐式提升为 Decimal,因为 Decimal 不是整数类型,并且出于性能原因。

在您的第二个代码示例中,由于您已经预先限定了操作数,您可以安全地将操作数类型转换为不跨越两个操作数范围的通用整数类型。另请注意,在您的第二个示例中,您可以将类型转换为字节(而不是长)而不会丢失信息,因为您已经确定 ulong 值小于 127 并且 sbyte 值是非负数。

C# 编译器不会“看到”您已经对操作数进行了预限定,并且从逻辑上讲,操作数中的值在字节范围内,并且编译器本身不会生成执行此类预限定的代码。

某些语言确实会发出类似于您的第二个示例的预限定代码,以支持不具有公共超集类型的类型之间的比较。为此,您会受到性能和内存(代码大小)的影响。本着不想“奖励”不良编码实践的精神,C# 可能不会发出这种预限定代码。如果您要比较有符号值和 ulong,则需要了解并承担成本。

在语言理论中有一个类型推断的分支,称为(我认为)类型代数,它确实跟踪针对变量的测试,并在代码流中发现新的约束时动态地缩小变量类型的范围。这种形式的类型推断将允许您在第二个示例中比较操作数而无需进行类型转换,因为它会看到您已将操作数预限定为字节范围。 C# 不做这种类型推断。我认为 Haskell 或 F# 可能。

【讨论】:

  • +1 很好的答案,非常完整。我假设你的意思是最后的“Haskell”而不是“Hascal”,除非有一个我无法通过谷歌找到的奇怪的 Pascal 变体。 :) 此外,如果您引用以下声明会更好:“出于性能原因,不包括隐式提升到 Decimal。”
  • Decimal 类型有利于完整性/精度而不是性能。整数类型编译为本机 CPU 上的硬件寄存器值。十进制没有。因此,Decimal 永远不会提供与整数类型相同级别的运行时性能。 Decimal 确实定义了从整数的隐式转换,但只有当表达式中的操作数之一是 Decimal 时,编译器才会考虑这种转换。 C# 编译器不认为 Decimal 是整数类型。
  • 这是一个非常好的答案。我被撕裂了。 Jon Skeet 是 Jon Skeet,但您的回答非常全面且易于理解。我凭直觉走!
  • 关闭,但没有雪茄。您关于十进制不是整数类型的论点是似是而非的。详情见我的回答。
  • 感谢您的澄清,埃里克。
猜你喜欢
  • 1970-01-01
  • 2015-05-31
  • 2018-07-12
  • 2020-10-10
  • 1970-01-01
  • 1970-01-01
  • 2010-12-30
  • 2015-11-30
  • 1970-01-01
相关资源
最近更新 更多