<p>I just thought about the possibility for the less-than and-greater than operators to short-circuit. That is, they can skip evaluating their second operand if the value of the second operand logically makes no impact on the overall result. Short-circuit evaluation is generally limited to logical OR and logical AND, because for OR, if the first operand is true, the result will be true regardless the value of the second operand, so it does not need to be evaluated. Similar goes for logical AND.</p>
<p>However, what if the first operand of a less-than-or-equal happens to be the minimum value of a type? Or if the first operand of a greater-than happens to be said minimum value? In these cases, there is no value of the second operand that will make the value anything other than true or false respectively. Here are more examples:</p>
<pre><code>INT_MIN &lt;= x; // true
INT_MIN &gt; x; // false
INT_MAX &gt;= x; // true
INT_MAX &lt; x; // false
</code></pre>
<p>Note that here <code>INT_MIN</code> or <code>INT_MAX</code> indicate the values at runtime, not compile-time constants.</p>
<p>These expressions will hold no matter the value of X. So why is it that no language I have know of seems to have short-circuiting behavior for relational comparisons?</p>

<operators><comparison><short-circuit-evaluation>
Why do relational comparison operators never short-circuit?
CC BY-SA 4.0
2023-10-02T00:11:58.217Z
What do you mean by short circuiting? Do you want to write `b = INT_MIN <= ;` instead of either `b = true ;` or `b = INT_MIN <= x;`?  Or do you simply want the result to be computed at compile time?
jmoreno
CC BY-SA 4.0
2023-10-02T02:31:09.817Z
Of course the compilers simply use the proper truth value there; is that short enough? ;-)
Peter - Reinstate Monica
CC BY-SA 4.0
2023-10-02T17:54:51.780Z
I don't think this is short-circuiting; this is constant folding
Seggan
CC BY-SA 4.0
2023-10-02T17:56:39.533Z
@Seggan Nope. I was referring to values that are not compile time constants but ***could*** be `INT_MIN` etc. No where in the question did I say the left-hand operands were constants.
CPlus
<p>It is not the case that relational comparison operators <strong>never</strong> short-circuit in any language. We can define this toy Haskell type and its comparison relationship to illustrate:</p>
<pre class="lang-hs prettyprint-override"><code>newtype IndexList a = IndexList [Int] deriving (Show, Eq)
instance Ord (IndexList a) where
  IndexList [] &lt;= _ = True
  _ &lt;= IndexList [] = False
  IndexList (h1:t1) &lt;= IndexList (h2:t2) = if h1 /= h2 then h1 &lt; h2 else IndexList t1 &lt;= IndexList t2
</code></pre>
<p>This type is a specialised list of integers, and it's defined to compare lexicographically with empty lists less than all other values.
It could be any user-defined type, including those with nontrivial structure and comparison logic.</p>
<p>We can test whether this short-circuits or not:</p>
<pre class="lang-hs prettyprint-override"><code>ghci&gt; IndexList [] &lt;= undefined
True
ghci&gt; IndexList [1..] &lt;= IndexList []
False
</code></pre>
<p>If the right-hand side were evaluated, there would be an exception reported on the first line (because that's what <code>undefined</code> does when you evaluate it), and the second would never terminate if the left-hand side were fully evaluated.</p>
<p>I'm not sure which of those you'd consider short-circuiting, but in both one side of the relational operator is left unevaluated due to the result being predetermined by the other side. It's exactly how <code>False &amp;&amp; undefined</code> short-circuits too.</p>
<hr />
<p>This arises out of Haskell's non-strict (~lazy) evaluation, and any language following this evaluation strategy could have the same behaviour. It is possible to create it in R, for example, although a little more awkwardly. This is a more general effect than purely the relational operators, but they are an obvious case where it can come up. That generality also means many of the associated costs are already paid.</p>
<p>The built-in primitive types in GHC <em>aren't</em> defined this way (they implement the alternative <code>compare</code> method, which returns less/equal/greater and very infrequently has a short-circuitable answer), likely for efficiency of compilation in typical cases, but it can come up in other types. Any situation where there is a clear base case is liable to be defined in a similar way to above, and thus to short-circuit, as is any with recursive comparison logic (like the built-in list type).</p>

CC BY-SA 4.0
2023-09-30T21:52:00.457Z
2023-09-30T21:52:00.457Z
Michael Homer
CC BY-SA 4.0
2023-10-01T23:38:15.443Z
Haskell is a lazy functional language, so effectively *everything* short circuits -- there are no side effects and things are only computed when needed.  So there's notthing special about relations or booleans or anything else.
Chris Dodd
CC BY-SA 4.0
2023-10-02T18:52:32.147Z
@ChrisDodd You have to be careful with what you mean by "needed", though. If `(||)` had been defined as `False || x = x; True || True = True; True || False = True`, it wouldn't short-circuit, for example.
amalloy
<h3>You Might Surprise the Programmer by Preventing Side-Effects</h3>
<p>Let’s say your C compiler reads the line:</p>
<pre><code>if (printf(&quot;hello, world!&quot;) &gt; 0) {
</code></pre>
<p>and (metaphorically) thinks, “I’m so smart, I know for a fact that this implementation of <code>printf()</code> on this format string will always return a positive value.  So, the comparison is always true.  No need to call <code>printf()</code>!”  Is this the intended behavior?</p>
<p>Note: if you’re only considering the form of “short-circuiting” that calculates the left operand first, checks it at runtime, and evaluates the right operand only if necessary, this introduces a host of problems over and above the kind of optimization I’m talking about. (And, in that case, just flip the operands in my example.)</p>
<p>So your new language could only optimize out a pure functional expression, which cannot have observable side-effects.  It would only potentially be useful to do that if both sides of the comparison are not trivial.  Even then,</p>
<h3>This Optimization is Unlikely to Pay Off</h3>
<p>Checking for the maximum or minimum value of a type at runtime takes at least one compare and branch.  If the value is drawn from a uniform distribution over a 32-bit type, it will pay off only one time in 4.3 billion, and even then only if the other operand takes significantly more time to calculate than that.  And it isn’t always possible to predict whether, say, the memory it accesses is in the cache.</p>
<p>It’s a lot more common in the real world, though, for computations to have a much narrower range than the type that holds them, which means the check would never pay off.  If the left-hand side is <code>i % n</code>, a runtime check for whether it holds the maximum possible value is just a waste.</p>

CC BY-SA 4.0
2023-10-01T17:46:59.937Z
2023-10-01T22:41:43.490Z
2023-10-01T22:41:43.490Z
Davislor
CC BY-SA 4.0
2023-10-01T18:01:43.943Z
The example with `printf` doesn't seem related to short-circuiting, since with short-circuiting semantics it is only the right-hand-side which is conditionally evaluated; the left side will still always be evaluated. Your point would apply equally to a statement like `printf("hello, world!");` where the compiler would be just as wrong to say "this expression's result isn't used, so it doesn't need to be evaluated".
kaya3
CC BY-SA 4.0
2023-10-01T21:36:30.793Z
@kaya3 An optimizer would optimize either `0 <= returnsUnsigned()` or `returnsUnsigned() >= 0` the same. However, if you’re imagining short-circuiting that works exactly like `&&` and `||`, flip the operands.
Davislor
<p>For completeness, if the relational comparison can be decided at compile time (as in your examples <code>INT_MIN &lt;= x</code> and so on), rather than short-circuiting, some compilers flag it as probably an error and emit a warning such as &quot;comparison is always true&quot;.</p>

CC BY-SA 4.0
2023-10-02T13:18:20.387Z
2023-10-02T13:18:20.387Z
Pablo H
CC BY-SA 4.0
2023-10-02T18:09:40.893Z
Or the compiler just applies constant folding on it
Seggan
<p>You've only given examples of what are essentially degenerate cases of short-circuiting - cases where compile-time constants determine the result, and the test could therefore be struck out of the source code.</p>
<p>Like these cases:</p>
<pre><code>1 OR x    //always true
0 AND x   //always false
</code></pre>
<p>Unlike those cases, the real value of short-circuiting only arises when both operands are variables, and those variables are in turn both calculated, meaning that the cost of calculating one branch of the expression can be saved if another branch has already determined the result (given the properties of the operator which joins the two branches).</p>
<p>In other words, where you have an expression of the form:</p>
<pre><code>(w OP x) OP (y OP z)
</code></pre>
<p>Where the result of the first level of operators form the arguments for the second level (which is intended to be the short-circuiting level), and the question is whether the results of evaluating the first branch, make evaluating the second branch unnecessary.</p>
<p>In the case of operators which take Boolean operands and are amenable to short-circuiting, there are only two possible cases to consider in determining whether to short-circuit or not - one case means short-circuiting can occur, the other not.</p>
<p>Therefore, checking whether to short-circuit an operator with Boolean operands may be a reasonable penalty to save the cost of evaluating the second branch of the expression, because it's a simple one-case test to resolve the question, and the short-circuit probably happens often enough to be worthwhile trying.</p>
<p>However, with the range comparison operators on integers (and other number types), there are a large range of possible values, and only a small number of cases in which the short-circuit could be triggered (such as one operand being at the most extreme value). A case where the short-circuit is actually hit, perhaps wouldn't happen even once in a programmer's career.</p>
<p>In the unusual case where it would still be worthwhile to check for the possibility of short-circuiting, the programmer could roll their own - as they might well in the broader cases where a knowledge of the application logic makes it possible to short-circuit a series of evaluations.</p>

CC BY-SA 4.0
2023-10-01T07:57:40.527Z
2023-10-01T07:57:40.527Z
Steve
CC BY-SA 4.0
2023-10-01T10:15:53.637Z
The examples in the question are meant to illustrate the situations at runtime where short-circuiting could happen. The idea is `x <= y` would short-circuit if `x` evaluates to `INT_MIN`. Not just for `INT_MIN <= y` where the left-side is a compile-time constant.
kaya3
CC BY-SA 4.0
2023-10-01T15:34:35.403Z
@kaya3: If I'm understanding correctly, that turns one comparison into two when evaluating on normal hardware.  (Except when the RHS has no side-effects so compilers can skip the short-circuiting).  `if (x < foo())` would have to get compiled like C `if (x != INT_MIN && x < foo())`, with two compare-and-branch operations, the first one before a function call, the second after.  This is horrible, and as your answer says, will take extra branch-prediction entries and isn't free for front-end throughput.
Peter Cordes
CC BY-SA 4.0
2023-10-03T17:04:33.277Z
@peter: `if (x != INT_MAX && x < foo())`, the exceptional value for strict `<` is `INT_MAX` not `INT_MIN`
Ben Voigt
<h2>The saving is rare</h2>
<p>The benefit of short-circuiting is that you don't have to compute the second operand. But there is only one value the first operand can have where short-circuiting is possible ─ either <code>INT_MIN</code> or <code>INT_MAX</code> depending on the comparison operator ─ and the most commonly-compared types (ints and doubles) have so many possible values that this one specific value is very rare. Or in many cases, that specific value might never occur, due to what the number is supposed to represent. (But the compiler won't know this.)</p>
<p>But in order to short-circuit, another comparison like <code>== INT_MIN</code> must be done, and there must be a branch on the result of that comparison. Even if the branch predictor figures out that the short-circuiting branch is never (or very rarely) taken, that extra comparison isn't free, and it can only very rarely be beneficial to do it.</p>
<h2>The saved computation is usually cheap</h2>
<p>The way humans think about code, means that we tend to write things like <code>something &lt;= 100</code> or <code>something() &lt;= something_else</code>, where the second operand is a constant or a simple load. This way of writing code is much more common than e.g. <code>100 &gt;= something</code>.</p>
<p>So the second operand is usually fast to evaluate, and there is little benefit in eliding it; most of the time, evaluating the second operand will be faster than the extra <code>== INT_MIN</code> comparison you would have to do in order to short-circuit it.</p>
<h2>People expect the second operand's side-effects to be unconditional</h2>
<p>Suppose you have something like <code>stack.pop() &lt;= stack.pop()</code>, where the second operand has a side-effect. Humans who read or write code like this will expect two values to be popped from the stack. Short-circuiting behaviour in this case would be a source of subtle, rare bugs.</p>
<p>This argument only applies if short-circuiting is part of the semantics of the comparison operators; there'd be no such problem in an optimising compiler which only inserts the short-circuit when it can prove the second operand is side-effect-free.</p>

CC BY-SA 4.0
2023-09-30T20:50:44.437Z
2023-10-01T23:29:29.707Z
2023-10-01T23:29:29.707Z
kaya3
CC BY-SA 4.0
2023-10-01T14:29:14.460Z
While the intent is clear for `stack1.pop() <= stack2.pop()`, I should note that the order of the pop()s for the same stack is likely unspecified, and could be subject to change with a different compiler/version.
Ángel
CC BY-SA 4.0
2023-10-01T16:45:44.673Z
@Ángel It may be unspecified in old languages like C, but every modern language I know of specifies left-to-right evaluation order if it matters. I think C should be seen as an outlier in this regard.
kaya3
CC BY-SA 4.0
2023-10-01T20:16:54.553Z
The only way there would be savings without cost is at compile time, if one of the values is a constant maximum or minimum value for its type; then the optimization could be made without run-time cost. There would be no point for a developer to type out an expression like this, but it might result from a macro or some other code generation algorithm.
EvilSnack
CC BY-SA 4.0
2023-10-02T08:49:17.390Z
@kaya3 in C++ the order is also not specified (`operator <=` is not a sequence point). And just because most modern languages specify evaluation left-to-right doesn't mean that this is necessarily a good idea - IMO code where single statements have multiple interdependent side-effects are confusing and bug-prone regardless, so the proper thing is to split them out as two lines, making it clear what's going on.
leftaroundabout
CC BY-SA 4.0
2023-10-02T09:06:24.593Z
@leftaroundabout C++ definitely counts as an "old language like C" :-)
Bergi
CC BY-SA 4.0
2023-10-02T17:16:14.913Z
@leftaroundabout Languages which don't specify the behaviour of valid programs are confusing and bug-prone; languages which allow users to write bad code are (checks notes) all of them.
kaya3
CC BY-SA 4.0
2023-10-02T17:49:43.683Z
@kaya3 well, in a sense programs with UB in them _are not valid programs_, and should raise a compiler error. This being, again, IMO the right response for an expression `stack.pop() <= stack.pop()`. Alas, C and C++ compilers don't make an error for every UB, though modern ones are actually fairly good at warning about them. Does that prevent users from writing bad code? -Of course not, but it does prevent them from writing _certain kinds_ of bad code, while not really interfering with any good code. Seems like a win to me.
leftaroundabout
CC BY-SA 4.0
2023-10-02T17:55:46.307Z
@leftaroundabout Well sure, if you decide that "valid program" means something other than what it normally means then you can claim whatever you like. Your opinion about expressions with multiple side-effectful sub-expressions is noted, though you must also forbid expressions with one side-effect where this affects the value of another sub-expression, such as `stack.pop() + stack.size()`. In practice this is hard to track and likely forbids too much, but if you really wish for a language that strict then it's none of my business.
kaya3
CC BY-SA 4.0
2023-10-02T18:05:06.953Z
@kaya3 "In practice this is hard to track" - yes, side effects are generally hard to track, which is one of the reasons I prefer purely functional programming! In Haskell, changing `a <- pop stack; b <- pop stack; if a<=b then...` to `if pop stack <= pop stack then ...` is a type error.
leftaroundabout
CC BY-SA 4.0
2023-10-03T17:25:35.863Z
@kaya3: The C Standard notes that UB arises, among other things, from *non-portable or* erroneous constructs, and forbids it only within *strictly* conforming programs, allowing for the possibility that it may occur in programs that are correct and conforming but not fully portable nor *strictly* conforming.  As for the C++ Standard, it expressly notes that makes no attempt to define any kind of conformance for C programs, and portions of the Standard which appear to specify requirements for programs are intended as shorthand way of expressing requirements for implementations.
supercat
<p>The primary reason for the existence and popularity of <code>&amp;&amp;</code> and <code>||</code> is suppression of the right side effect, not efficiency. If you know that your hardware can evaluate both sides efficiently, it is likely faster to replace them with their eager variants. Most people do not understand this and I've encountered style guides as a professional developer that forced me to revert such changes.</p>
<p>The primary use case of such operators is something like <code>null == x || x.f()</code>.
Also, <code>&amp;&amp;</code> is essentially chaining of nested <code>if</code> statements without <code>else</code>.</p>
<p>Performing your proposed optimization at runtime is more expensive than the eager operator and I do not see any common pattern that would make it useful to drop the side effect in comparisons. Most index uses are offloaded to library functions anyway.</p>
<p>I would expect that most compilers will transform comparisons that are known to be true or false to just the side effect and the result.</p>

CC BY-SA 4.0
2023-10-01T07:11:20.370Z
2023-10-01T08:23:05.087Z
2023-10-01T08:23:05.087Z
feldentm
CC BY-SA 4.0
2023-10-01T08:04:28.980Z
*"suppression of the right side effect"* ... `null == x || x.f()` heavens to murgatroyd!
Steve
CC BY-SA 4.0
2023-10-01T08:22:13.940Z
@Steve Please elaborate.
feldentm
CC BY-SA 4.0
2023-10-01T08:49:18.690Z
What's surprising is the use of these operators to express conditional execution, in cases where full execution of the expression would actually produce incorrect results, and where presumably the actual result of the expression is discarded. It's very interesting, though not sure whether I like it.
Steve
CC BY-SA 4.0
2023-10-01T09:20:45.570Z
@Steve something like `if (null == incoming || incoming.isEmpty()) node.predecessors = emptySet else ...`
feldentm
CC BY-SA 4.0
2023-10-01T10:14:34.917Z
@Steve It's extremely common to write this kind of code (relying on short-circuiting to first check an operation is safe and then perform it) in Java, C#, Javascript/Typescript and many other languages; Typescript in particular is interesting because it does control-flow type narrowing including for short-circuiting logical operators, so `x !== undefined && x.foo()` eliminates `undefined` from `x` in the second operand, if it is a union type. I would guess that somewhere in the range of a quarter to three quarters of the time I use a short-circuiting logical operator, it's like this.
kaya3
CC BY-SA 4.0
2023-10-01T12:40:32.243Z
@feldentm, it seems more familiar in the concrete case of a series of checks in the header of an if block, with no side effects - rather than as a free-standing expression, with a side-effecting right-hand. Every day's a school day!
Steve
CC BY-SA 4.0
2023-10-01T12:55:12.257Z
@kaya3, indeed. I think what's thrown me for six here is the use of what I'm thinking of as a class of Boolean operators, for flow control. The more I think about it, the more it's clear I'm familiar with the idiomatic use for null checking - but I seem to have understood this more like try-catch, where it bails out of the evaluation in an error case, rather than generalising it as a form of conditional execution like if-then.
Steve
CC BY-SA 4.0
2023-10-01T12:58:24.723Z
Yes, this kind of expression normally appears in an `if`, `while` or other statement taking a condition. Though in dynamically-typed languages people do use them for things like `let x = something() || something_else();` where `something()` might return `null` (i.e. using `||` as a null-coalescing operator). Only minifiers tend to use the short-circuiting logical operators in free-standing expressions, and if a human wrote this (outside of the context of code golf) then it would be smelly.
kaya3
CC BY-SA 4.0
2023-10-02T02:36:36.160Z
This answer could be more direct: The question has it backwards. It is not "why do relational operators not short circuit" but *why on Earth* do the logical ones short-circuit if it is both slower and more complicated? Because you want to suppress the right evaluation in an elegant fashion.
Peter - Reinstate Monica
CC BY-SA 4.0
2023-10-02T18:19:23.237Z
@kaya3 in both modern C# and JS/TS the preferred way of doing this is the `?.` operator (AKA "Null Conditional Operator" or "optional chaining"), e.g. `maybeNull?.foo()` . Using the short-circuit operator has fallen out of favor, though I suppose it still has uses in C# where typing is a lot stronger.
Sean Sutherland
CC BY-SA 4.0
2023-10-02T20:10:16.933Z
@SeanSutherland Preferred by some, but I prefer the version which is more explicit about what should happen when the value is undefined. With `?.` I always have to think about whether the condition is equivalent to `x === undefined || x.foo()` or `x !== undefined && x.foo()`. There's also the fact that in Typescript, `x?.foo()` would return `boolean | undefined` which isn't assignable to `boolean`, so you can't use that version in some contexts.
kaya3
CC BY-SA 4.0
2023-10-02T21:23:57.080Z
It might be possible to reap similar benefits to those sought, but in a broader range of contexts, if a language had an operator which, given two expressions, may yield a compile-time constant equal to the first operand if it can, and otherwise yield the second.  This would allow for `(x > 500 ??? 0) || (f() < x)` to evaluate `f() < x`, but with the provision that if `x` is 500 or more the comparison may be presumed true.
supercat
CC BY-SA 4.0
2023-10-03T13:38:19.080Z
@supercat and which actual problem would that solve?
feldentm
CC BY-SA 4.0
2023-10-03T15:24:42.607Z
@feldentm: The the `x > 500` condition might not be true often enough at runtime to make the comparison worth the runtime cost.  Performing the conditional test statically when possible, however, would never have a positive runtime cost but still sometimes have a negative one.  Some compilers bend over backward to find places where the Standard would allow them to statically resolve conditionals, but the places where the Standard would allow that those seldom correspond to the places where such optimizations would actually be useful.
supercat
# Why do relational comparison operators never short-circuit?
Posted by: username_0 on 2023-09-30
I just thought about the possibility for the less-than and-greater than operators to short-circuit. That is, they can skip evaluating their second operand if the value of the second operand logically makes no impact on the overall result. Short-circuit evaluation is generally limited to logical OR and logical AND, because for OR, if the first operand is true, the result will be true regardless the value of the second operand, so it does not need to be evaluated. Similar goes for logical AND.
However, what if the first operand of a less-than-or-equal happens to be the minimum value of a type? Or if the first operand of a greater-than happens to be said minimum value? In these cases, there is no value of the second operand that will make the value anything other than true or false respectively. Here are more examples:
INT_MIN <= x; // true
INT_MIN > x; // false
INT_MAX >= x; // true
INT_MAX < x; // false

Note that here INT_MIN or INT_MAX indicate the values at runtime, not compile-time constants.
These expressions will hold no matter the value of X. So why is it that no language I have know of seems to have short-circuiting behavior for relational comparisons?
**Tags:** operators comparison short-circuit-evaluation
> **Comments:**
> **username_1**: What do you mean by short circuiting? Do you want to write `b = INT_MIN <= ;` instead of either `b = true ;` or `b = INT_MIN <= x;`?  Or do you simply want the result to be computed at compile time?  
> **username_2**: Of course the compilers simply use the proper truth value there; is that short enough? ;-)  
> **username_3**: I don't think this is short-circuiting; this is constant folding  
> **username_0**: @Seggan Nope. I was referring to values that are not compile time constants but ***could*** be `INT_MIN` etc. No where in the question did I say the left-hand operands were constants.  
---
## 6 answers
### Answer by username_8 on 2023-09-30. Score: 31 | Accepted answer
## The saving is rare

The benefit of short-circuiting is that you don't have to compute the second operand. But there is only one value the first operand can have where short-circuiting is possible ─ either `INT_MIN` or `INT_MAX` depending on the comparison operator ─ and the most commonly-compared types (ints and doubles) have so many possible values that this one specific value is very rare. Or in many cases, that specific value might never occur, due to what the number is supposed to represent. (But the compiler won't know this.)

But in order to short-circuit, another comparison like `== INT_MIN` must be done, and there must be a branch on the result of that comparison. Even if the branch predictor figures out that the short-circuiting branch is never (or very rarely) taken, that extra comparison isn't free, and it can only very rarely be beneficial to do it.

## The saved computation is usually cheap

The way humans think about code, means that we tend to write things like `something <= 100` or `something() <= something_else`, where the second operand is a constant or a simple load. This way of writing code is much more common than e.g. `100 >= something`.

So the second operand is usually fast to evaluate, and there is little benefit in eliding it; most of the time, evaluating the second operand will be faster than the extra `== INT_MIN` comparison you would have to do in order to short-circuit it.

## People expect the second operand's side-effects to be unconditional

Suppose you have something like `stack.pop() <= stack.pop()`, where the second operand has a side-effect. Humans who read or write code like this will expect two values to be popped from the stack. Short-circuiting behaviour in this case would be a source of subtle, rare bugs.

This argument only applies if short-circuiting is part of the semantics of the comparison operators; there'd be no such problem in an optimising compiler which only inserts the short-circuit when it can prove the second operand is side-effect-free.
> **Comments:**
> **username_13** (2023-10-01): While the intent is clear for `stack1.pop() <= stack2.pop()`, I should note that the order of the pop()s for the same stack is likely unspecified, and could be subject to change with a different compiler/version.  
> **username_8** (2023-10-01): @Ángel It may be unspecified in old languages like C, but every modern language I know of specifies left-to-right evaluation order if it matters. I think C should be seen as an outlier in this regard.  
> **username_14** (2023-10-01): The only way there would be savings without cost is at compile time, if one of the values is a constant maximum or minimum value for its type; then the optimization could be made without run-time cost. There would be no point for a developer to type out an expression like this, but it might result from a macro or some other code generation algorithm.  
> **username_15** (2023-10-02): @kaya3 in C++ the order is also not specified (`operator <=` is not a sequence point). And just because most modern languages specify evaluation left-to-right doesn't mean that this is necessarily a good idea - IMO code where single statements have multiple interdependent side-effects are confusing and bug-prone regardless, so the proper thing is to split them out as two lines, making it clear what's going on.  
> **username_16** (2023-10-02): @leftaroundabout C++ definitely counts as an "old language like C" :-)  
> **username_8** (2023-10-02): @leftaroundabout Languages which don't specify the behaviour of valid programs are confusing and bug-prone; languages which allow users to write bad code are (checks notes) all of them.  
> **username_15** (2023-10-02): @kaya3 well, in a sense programs with UB in them _are not valid programs_, and should raise a compiler error. This being, again, IMO the right response for an expression `stack.pop() <= stack.pop()`. Alas, C and C++ compilers don't make an error for every UB, though modern ones are actually fairly good at warning about them. Does that prevent users from writing bad code? -Of course not, but it does prevent them from writing _certain kinds_ of bad code, while not really interfering with any good code. Seems like a win to me.  
> **username_8** (2023-10-02): @leftaroundabout Well sure, if you decide that "valid program" means something other than what it normally means then you can claim whatever you like. Your opinion about expressions with multiple side-effectful sub-expressions is noted, though you must also forbid expressions with one side-effect where this affects the value of another sub-expression, such as `stack.pop() + stack.size()`. In practice this is hard to track and likely forbids too much, but if you really wish for a language that strict then it's none of my business.  
> **username_15** (2023-10-02): @kaya3 "In practice this is hard to track" - yes, side effects are generally hard to track, which is one of the reasons I prefer purely functional programming! In Haskell, changing `a <- pop stack; b <- pop stack; if a<=b then...` to `if pop stack <= pop stack then ...` is a type error.  
> **username_17** (2023-10-03): @kaya3: The C Standard notes that UB arises, among other things, from *non-portable or* erroneous constructs, and forbids it only within *strictly* conforming programs, allowing for the possibility that it may occur in programs that are correct and conforming but not fully portable nor *strictly* conforming.  As for the C++ Standard, it expressly notes that makes no attempt to define any kind of conformance for C programs, and portions of the Standard which appear to specify requirements for programs are intended as shorthand way of expressing requirements for implementations.  
---
### Answer by username_18 on 2023-10-01. Score: 11
The primary reason for the existence and popularity of `&&` and `||` is suppression of the right side effect, not efficiency. If you know that your hardware can evaluate both sides efficiently, it is likely faster to replace them with their eager variants. Most people do not understand this and I've encountered style guides as a professional developer that forced me to revert such changes.

The primary use case of such operators is something like `null == x || x.f()`.
Also, `&&` is essentially chaining of nested `if` statements without `else`.

Performing your proposed optimization at runtime is more expensive than the eager operator and I do not see any common pattern that would make it useful to drop the side effect in comparisons. Most index uses are offloaded to library functions anyway.

I would expect that most compilers will transform comparisons that are known to be true or false to just the side effect and the result.
> **Comments:**
> **username_10** (2023-10-01): *"suppression of the right side effect"* ... `null == x || x.f()` heavens to murgatroyd!  
> **username_18** (2023-10-01): @Steve Please elaborate.  
> **username_10** (2023-10-01): What's surprising is the use of these operators to express conditional execution, in cases where full execution of the expression would actually produce incorrect results, and where presumably the actual result of the expression is discarded. It's very interesting, though not sure whether I like it.  
> **username_18** (2023-10-01): @Steve something like `if (null == incoming || incoming.isEmpty()) node.predecessors = emptySet else ...`  
> **username_8** (2023-10-01): @Steve It's extremely common to write this kind of code (relying on short-circuiting to first check an operation is safe and then perform it) in Java, C#, Javascript/Typescript and many other languages; Typescript in particular is interesting because it does control-flow type narrowing including for short-circuiting logical operators, so `x !== undefined && x.foo()` eliminates `undefined` from `x` in the second operand, if it is a union type. I would guess that somewhere in the range of a quarter to three quarters of the time I use a short-circuiting logical operator, it's like this.  
> **username_10** (2023-10-01): @feldentm, it seems more familiar in the concrete case of a series of checks in the header of an if block, with no side effects - rather than as a free-standing expression, with a side-effecting right-hand. Every day's a school day!  
> **username_10** (2023-10-01): @kaya3, indeed. I think what's thrown me for six here is the use of what I'm thinking of as a class of Boolean operators, for flow control. The more I think about it, the more it's clear I'm familiar with the idiomatic use for null checking - but I seem to have understood this more like try-catch, where it bails out of the evaluation in an error case, rather than generalising it as a form of conditional execution like if-then.  
> **username_8** (2023-10-01): Yes, this kind of expression normally appears in an `if`, `while` or other statement taking a condition. Though in dynamically-typed languages people do use them for things like `let x = something() || something_else();` where `something()` might return `null` (i.e. using `||` as a null-coalescing operator). Only minifiers tend to use the short-circuiting logical operators in free-standing expressions, and if a human wrote this (outside of the context of code golf) then it would be smelly.  
> **username_2** (2023-10-02): This answer could be more direct: The question has it backwards. It is not "why do relational operators not short circuit" but *why on Earth* do the logical ones short-circuit if it is both slower and more complicated? Because you want to suppress the right evaluation in an elegant fashion.  
> **username_19** (2023-10-02): @kaya3 in both modern C# and JS/TS the preferred way of doing this is the `?.` operator (AKA "Null Conditional Operator" or "optional chaining"), e.g. `maybeNull?.foo()` . Using the short-circuit operator has fallen out of favor, though I suppose it still has uses in C# where typing is a lot stronger.  
> **username_8** (2023-10-02): @SeanSutherland Preferred by some, but I prefer the version which is more explicit about what should happen when the value is undefined. With `?.` I always have to think about whether the condition is equivalent to `x === undefined || x.foo()` or `x !== undefined && x.foo()`. There's also the fact that in Typescript, `x?.foo()` would return `boolean | undefined` which isn't assignable to `boolean`, so you can't use that version in some contexts.  
> **username_17** (2023-10-02): It might be possible to reap similar benefits to those sought, but in a broader range of contexts, if a language had an operator which, given two expressions, may yield a compile-time constant equal to the first operand if it can, and otherwise yield the second.  This would allow for `(x > 500 ??? 0) || (f() < x)` to evaluate `f() < x`, but with the provision that if `x` is 500 or more the comparison may be presumed true.  
> **username_18** (2023-10-03): @supercat and which actual problem would that solve?  
> **username_17** (2023-10-03): @feldentm: The the `x > 500` condition might not be true often enough at runtime to make the comparison worth the runtime cost.  Performing the conditional test statically when possible, however, would never have a positive runtime cost but still sometimes have a negative one.  Some compilers bend over backward to find places where the Standard would allow them to statically resolve conditionals, but the places where the Standard would allow that those seldom correspond to the places where such optimizations would actually be useful.  
---
### Answer by username_4 on 2023-09-30. Score: 6
It is not the case that relational comparison operators never short-circuit in any language. We can define this toy Haskell type and its comparison relationship to illustrate:
newtype IndexList a = IndexList [Int] deriving (Show, Eq)
instance Ord (IndexList a) where
  IndexList [] <= _ = True
  _ <= IndexList [] = False
  IndexList (h1:t1) <= IndexList (h2:t2) = if h1 /= h2 then h1 < h2 else IndexList t1 <= IndexList t2

This type is a specialised list of integers, and it's defined to compare lexicographically with empty lists less than all other values.
It could be any user-defined type, including those with nontrivial structure and comparison logic.
We can test whether this short-circuits or not:
ghci> IndexList [] <= undefined
True
ghci> IndexList [1..] <= IndexList []
False

If the right-hand side were evaluated, there would be an exception reported on the first line (because that's what undefined does when you evaluate it), and the second would never terminate if the left-hand side were fully evaluated.
I'm not sure which of those you'd consider short-circuiting, but in both one side of the relational operator is left unevaluated due to the result being predetermined by the other side. It's exactly how False && undefined short-circuits too.

This arises out of Haskell's non-strict (~lazy) evaluation, and any language following this evaluation strategy could have the same behaviour. It is possible to create it in R, for example, although a little more awkwardly. This is a more general effect than purely the relational operators, but they are an obvious case where it can come up. That generality also means many of the associated costs are already paid.
The built-in primitive types in GHC aren't defined this way (they implement the alternative compare method, which returns less/equal/greater and very infrequently has a short-circuitable answer), likely for efficiency of compilation in typical cases, but it can come up in other types. Any situation where there is a clear base case is liable to be defined in a similar way to above, and thus to short-circuit, as is any with recursive comparison logic (like the built-in list type).
> **Comments:**
> **username_5** (2023-10-01): Haskell is a lazy functional language, so effectively *everything* short circuits -- there are no side effects and things are only computed when needed.  So there's notthing special about relations or booleans or anything else.  
> **username_6** (2023-10-02): @ChrisDodd You have to be careful with what you mean by "needed", though. If `(||)` had been defined as `False || x = x; True || True = True; True || False = True`, it wouldn't short-circuit, for example.  
---
### Answer by username_7 on 2023-10-01. Score: 3
You Might Surprise the Programmer by Preventing Side-Effects
Let’s say your C compiler reads the line:
if (printf("hello, world!") > 0) {

and (metaphorically) thinks, “I’m so smart, I know for a fact that this implementation of printf() on this format string will always return a positive value.  So, the comparison is always true.  No need to call printf()!”  Is this the intended behavior?
Note: if you’re only considering the form of “short-circuiting” that calculates the left operand first, checks it at runtime, and evaluates the right operand only if necessary, this introduces a host of problems over and above the kind of optimization I’m talking about. (And, in that case, just flip the operands in my example.)
So your new language could only optimize out a pure functional expression, which cannot have observable side-effects.  It would only potentially be useful to do that if both sides of the comparison are not trivial.  Even then,
This Optimization is Unlikely to Pay Off
Checking for the maximum or minimum value of a type at runtime takes at least one compare and branch.  If the value is drawn from a uniform distribution over a 32-bit type, it will pay off only one time in 4.3 billion, and even then only if the other operand takes significantly more time to calculate than that.  And it isn’t always possible to predict whether, say, the memory it accesses is in the cache.
It’s a lot more common in the real world, though, for computations to have a much narrower range than the type that holds them, which means the check would never pay off.  If the left-hand side is i % n, a runtime check for whether it holds the maximum possible value is just a waste.
> **Comments:**
> **username_8** (2023-10-01): The example with `printf` doesn't seem related to short-circuiting, since with short-circuiting semantics it is only the right-hand-side which is conditionally evaluated; the left side will still always be evaluated. Your point would apply equally to a statement like `printf("hello, world!");` where the compiler would be just as wrong to say "this expression's result isn't used, so it doesn't need to be evaluated".  
> **username_7** (2023-10-01): @kaya3 An optimizer would optimize either `0 <= returnsUnsigned()` or `returnsUnsigned() >= 0` the same. However, if you’re imagining short-circuiting that works exactly like `&&` and `||`, flip the operands.  
---
### Answer by username_9 on 2023-10-02. Score: 2
For completeness, if the relational comparison can be decided at compile time (as in your examples `INT_MIN <= x` and so on), rather than short-circuiting, some compilers flag it as probably an error and emit a warning such as "comparison is always true".
> **Comments:**
> **username_3** (2023-10-02): Or the compiler just applies constant folding on it  
---
### Answer by username_10 on 2023-10-01. Score: 2
You've only given examples of what are essentially degenerate cases of short-circuiting - cases where compile-time constants determine the result, and the test could therefore be struck out of the source code.
Like these cases:
1 OR x    //always true
0 AND x   //always false

Unlike those cases, the real value of short-circuiting only arises when both operands are variables, and those variables are in turn both calculated, meaning that the cost of calculating one branch of the expression can be saved if another branch has already determined the result (given the properties of the operator which joins the two branches).
In other words, where you have an expression of the form:
(w OP x) OP (y OP z)

Where the result of the first level of operators form the arguments for the second level (which is intended to be the short-circuiting level), and the question is whether the results of evaluating the first branch, make evaluating the second branch unnecessary.
In the case of operators which take Boolean operands and are amenable to short-circuiting, there are only two possible cases to consider in determining whether to short-circuit or not - one case means short-circuiting can occur, the other not.
Therefore, checking whether to short-circuit an operator with Boolean operands may be a reasonable penalty to save the cost of evaluating the second branch of the expression, because it's a simple one-case test to resolve the question, and the short-circuit probably happens often enough to be worthwhile trying.
However, with the range comparison operators on integers (and other number types), there are a large range of possible values, and only a small number of cases in which the short-circuit could be triggered (such as one operand being at the most extreme value). A case where the short-circuit is actually hit, perhaps wouldn't happen even once in a programmer's career.
In the unusual case where it would still be worthwhile to check for the possibility of short-circuiting, the programmer could roll their own - as they might well in the broader cases where a knowledge of the application logic makes it possible to short-circuit a series of evaluations.
> **Comments:**
> **username_8** (2023-10-01): The examples in the question are meant to illustrate the situations at runtime where short-circuiting could happen. The idea is `x <= y` would short-circuit if `x` evaluates to `INT_MIN`. Not just for `INT_MIN <= y` where the left-side is a compile-time constant.  
> **username_11** (2023-10-01): @kaya3: If I'm understanding correctly, that turns one comparison into two when evaluating on normal hardware.  (Except when the RHS has no side-effects so compilers can skip the short-circuiting).  `if (x < foo())` would have to get compiled like C `if (x != INT_MIN && x < foo())`, with two compare-and-branch operations, the first one before a function call, the second after.  This is horrible, and as your answer says, will take extra branch-prediction entries and isn't free for front-end throughput.  
> **username_12** (2023-10-03): @peter: `if (x != INT_MAX && x < foo())`, the exceptional value for strict `<` is `INT_MAX` not `INT_MIN`  
---
