char_x < (char_y + 1) == char_x <= char_y?

11

大家好,我在浏览一些Java源代码时遇到了这个(java.lang.Character):

public static boolean isHighSurrogate(char ch) {
    return ch >= MIN_HIGH_SURROGATE && ch < (MAX_HIGH_SURROGATE + 1);
}

public static boolean isLowSurrogate(char ch) {
    return ch >= MIN_LOW_SURROGATE && ch < (MAX_LOW_SURROGATE + 1);
}

我想知道为什么作者在高限制上加了1并进行小于比较,而不是简单地进行小于等于比较?

如果这有助于可读性,我可以理解,但在这种情况下似乎并非如此。

我想知道上面的代码与下面代码之间有什么区别:

public static boolean isHighSurrogate(char ch) {
    return ch >= MIN_HIGH_SURROGATE && ch <= MAX_HIGH_SURROGATE;
}

public static boolean isLowSurrogate(char ch) {
    return ch >= MIN_LOW_SURROGATE && ch <= MAX_LOW_SURROGATE;
}

这只是一个猜测,不是答案:也许是为了与编写for循环的习惯用法保持一致,例如 for (i = LOWER_BOUND; i < HIGHER_BOUND; i++) - Oliver Charlesworth
也许它的目的是强调 ch 也可以等于 MAX_[HIGH|LOW]_SURROGATE。"<=" 更容易被误读为 "<" - 而且编译器可能会将其优化为相同的字节码。 - S.L. Barth
2
首先,MAX_HIGH_SURROGATE 是一个 char 类型,而 1 是一个 int 类型... 可能需要进行一些从 char 到 int 的转换。 - BoltClock
没事,我看错了代码。反正我的回答本来也应该是评论。 - Dave
4个回答

2

猜测

代理字符,是Unicode编码中的一系列代码点,用于UTF-16中配对表示基本多文种平面以外字符。

在我看来,他想忽略8位的内容。这意味着如果最大值为0xFF,那么0xFF+1将会溢出并返回0x00,使比较始终为假。

因此,如果使用8位的字符编译代码,它将始终返回false(超出了UTF-16范围),而如果编译一个>8位的字符,则0xFF+1将为0x100并仍然可以正常工作。

希望这对您有所帮助。


在Java中,char始终是一个无符号16位整数。最大值为0xDBFF,最小值为0xD800。 - Mike Samuel

2

或许作者试图遵循Dijkstra的建议,将所有范围都设为半开区间:起始点包含在内,终止点不包含在内。

这里没有语义上的差别,但字节码中存在微妙的差异:(ch + 1) 是一个 int,因此第一个代码片段执行了一个从 charchar 的比较,然后是从 intint 的比较,而第二个则执行了两个从 charchar 的比较。这并不会导致语义上的差异--隐式强制类型转换到更宽的类型,因此两个代码片段均不存在溢出风险。

优化掉加法并将 int 转换回 2 字节无符号的 int 比较,在 JIT 所做的优化范围内。因此,我不认为有任何特定的性能原因偏好其中一个。

我倾向于编写这种东西:

MIN_LOW_SURROGATE <= ch && ch <= MAX_LOW_SURROGATE

那么,在中间使用ch会让读者明显地知道ch正被测试其是否在外部值的范围内。

我认为如果他在两边都使用相同的比较,即 ch >= (MIN_HIGH_SURROGATE + 0) && ch < (MAX_HIGH_SURROGATE + 1),那么这将更有意义。 - Pacerier
@Pacerier,表达同一件事情的方式有很多微妙的不同之处。我会选择让下溢/上溢和偏移1个错误更加突出的方式,并坚持使用它。 - Mike Samuel

0

我觉得这两种编码方式实际上并没有什么区别,只是一个取决于个人喜好而已。因为这两种不同的实现并没有明显的优势。

我想知道为什么作者要在高限制上加1,并做小于比较,而不是直接做小于等于比较呢?

我的意思是,为什么你会倾向于第二种选择呢?我有什么地方理解错了吗?


我的意思是,第二个选择对你来说更易读(更有逻辑意义),不是吗? - Pacerier
不,实际上并不会,因为我期望MAX和MIN之间至少有1的差异,而+1则使这一点变得清晰明了,但这只是我的观点。 - philomatic

-1

因为作者是C++或汇编语言的专家。

使用>=比使用>更快,使用<=比使用<更快。实际上,当你写a<b时,编译器会将其转换为a<=b+1,因为唯一可用的汇编指令是<=。如果你手动在代码中写这个求和,C++编译器会在编译时将MIN_HIGH_SURROGATE + 1替换为实际结果的值。这样你就可以节省一条指令和一个周期。

但是所有这些奇怪的推理只适用于编译后的代码,如C++或C。或者汇编语言。

编辑

虽然每个等式运算符都有相应的指令(我错了),但它们都归结为微码中的减法和(如果需要)加法。然后处理器检查结果的符号位。因此,上述代码公式仍然更快。

为了确保在加1时没有溢出,微处理器首先执行减法,然后再加1。


你必须确保这种情况不会发生。这是程序员的职责。 - Sam
在x86上,至少有<=<的指令。Java字节码也是如此。两者都不应该比另一个更快。因此,我怀疑编译器会将其中一个转换为另一个。 - Oliver Charlesworth
所以我可能是错的... 另一方面,汇编语言 < 可能会被微码转换为 <= r+1。 - Sam
这是在哪个神话般的CPU上运行的?至少不是x86。 - harold
@OliCharleswort、pacerier和harold,感谢你们的批评,你们帮助我重新检查并找到了正确的答案。 - Sam
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接