Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Multi-compare operators

274 views
Skip to first unread message

Rick C. Hodgin

unread,
Sep 19, 2018, 11:01:58 AM9/19/18
to
Can anyone think of a reason why this type of operation shouldn't
be a valid syntax in a (C/C++)-like lanuage, added to that new
language with these features:

if (a <= b < c)
printf("b is [a, c)\n");

if (a < b <= c)
printf("b is (a, c]\n");

if (a < b < c)
printf("b is (a, c)\n");

And even these:

if (a < b < c < d)
prinf("b is (a, c)\nc is (b, d)\n");

if (a < b < c < d < e)
prinf("b is (a, c)\nc is (b, d)\nd is (c, e)\n");

Et cetera... Each evaluated left-to-right.

--
Rick C. Hodgin

Scott

unread,
Sep 19, 2018, 12:23:15 PM9/19/18
to
On Wed, 19 Sep 2018 11:01:45 -0400, "Rick C. Hodgin"
<rick.c...@gmail.com> wrote:

>Can anyone think of a reason why this type of operation shouldn't
>be a valid syntax in a (C/C++)-like lanuage, added to that new
>language with these features:
>
> if (a <= b < c)
> printf("b is [a, c)\n");
>
> if (a < b <= c)
> printf("b is (a, c]\n");
>
> if (a < b < c)
> printf("b is (a, c)\n");
>
>And even these:
>
> if (a < b < c < d)
> prinf("b is (a, c)\nc is (b, d)\n");
>
> if (a < b < c < d < e)
> prinf("b is (a, c)\nc is (b, d)\nd is (c, e)\n");
...

What nonsense are you trolling about now? Everybody knows that these
are already perfectly valid constructs in C with simple and
unambiguous semantics.

Ike Naar

unread,
Sep 19, 2018, 1:11:03 PM9/19/18
to
It would break existing code, for instance

0 < 1 == 0 < 1

is parsed (in current C) as

(0 < 1) == (0 < 1)

which evaluates to 1 (true).
According to your proposal it would be parsed as

(0 < 1) && (1 == 0) && (0 < 1)

which evaluates to 0 (false).

Rick C. Hodgin

unread,
Sep 19, 2018, 1:32:56 PM9/19/18
to
No. This new syntax would only work with forms which are con-
sistent with < or <= operators across from left-to-right, and
I would also probably place a constraint on it that they be
either stand-alone in an expression, or enclosed with paren-
thesis.

It would not include other operators like !=, ==, > or >=.

For CAlive I would probably also include <# and <=#, where the
# indicates that the comparison should only be done to so many
decimal digits:

float#2 fVal1;
float#4 fVal2;

// These values will hold whatever they hold, but whenever
// it encounters a # operator (==#, <#, <=#, >#, >=#), it
// will only conduct the comparison out to the minimum of
// the indicated decimal places

--
Rick C. Hodgin

bitrex

unread,
Sep 19, 2018, 1:43:56 PM9/19/18
to
it's ambiguous enough in that gcc emits a warning:

"warning: comparisons like X <= Y <= Z do not have their mathematical
meaning [-Wparenthesis]"

that is to say if (a <= b < c) will evaluate to if ((a <= b) < c) -> if
((bool) < c) which is probably not what you want, you often want if (a
<= b && b < c)

Rick C. Hodgin

unread,
Sep 19, 2018, 1:54:10 PM9/19/18
to
On 9/19/2018 1:32 PM, Rick C. Hodgin wrote:
> I would also probably place a constraint on it that they be
> either stand-alone in an expression, or enclosed with paren-
> thesis.


I could define it to require braces, so it would then also
work with C/C++ because they could add those features as
well:

if ({a < b < c})
printf("b is (a,c)\n");

if ({a < b < c < d} || a+b+c+d == 0)
printf("...\n");

That might work.

--
Rick C. Hodgin

Keith Thompson

unread,
Sep 19, 2018, 2:18:31 PM9/19/18
to
bitrex <us...@example.net> writes:
[...]
> it's ambiguous enough in that gcc emits a warning:
>
> "warning: comparisons like X <= Y <= Z do not have their mathematical
> meaning [-Wparenthesis]"
>
> that is to say if (a <= b < c) will evaluate to if ((a <= b) < c) -> if
> ((bool) < c) which is probably not what you want, you often want
> if (a <= b && b < c)

A small quibble: C's equality and relational operators yield a
result of type int with the value 0 or 1, not a bool (or _Bool).
(In C++ they yield a result of type bool.)

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Mr Flibble

unread,
Sep 19, 2018, 3:22:23 PM9/19/18
to
Nobody is interested in this fucktarded idea of yours mate so give it a rest.

/Flibble

--
"Suppose it’s all true, and you walk up to the pearly gates, and are
confronted by God," Bryne asked on his show The Meaning of Life. "What
will Stephen Fry say to him, her, or it?"
"I’d say, bone cancer in children? What’s that about?" Fry replied.
"How dare you? How dare you create a world to which there is such misery
that is not our fault. It’s not right, it’s utterly, utterly evil."
"Why should I respect a capricious, mean-minded, stupid God who creates a
world that is so full of injustice and pain. That’s what I would say."

jacobnavia

unread,
Sep 19, 2018, 4:26:24 PM9/19/18
to
Le 19/09/2018 à 21:22, Mr Flibble a écrit :
> On 19/09/2018 18:53, Rick C. Hodgin wrote:
>> On 9/19/2018 1:32 PM, Rick C. Hodgin wrote:
>>> I would also probably place a constraint on it that they be
>>> either stand-alone in an expression, or enclosed with paren-
>>> thesis.
>>
>>
>> I could define it to require braces, so it would then also
>> work with C/C++ because they could add those features as
>> well:
>>
>>      if ({a < b < c})
>>          printf("b is (a,c)\n");
>>
>>      if ({a < b < c < d} || a+b+c+d == 0)
>>          printf("...\n");
>>
>> That might work.
>
> Nobody is interested in this fucktarded idea of yours mate so give it a
> rest.
>
> /Flibble
>

This guy is trolling since several YEARS about his new "language"... He
has NEVER presented ANYTHING that actually runs, all that is VAPORWARE
that is trying to hide the fact that he is spamming these newsgroups
with religious shit!!!

But there are ALWAYS people that answer his supposedely "technical"
questions...

DO NOT FEED THE TROLLS DAMM IT!

Rick C. Hodgin

unread,
Sep 19, 2018, 4:41:28 PM9/19/18
to
On 9/19/2018 4:26 PM, jacobnavia wrote:
> This guy is trolling since several YEARS about his new "language"... He has
> NEVER presented ANYTHING that actually runs, all that is VAPORWARE that is
> trying to hide the fact that he is spamming these newsgroups with religious
> ..!!!

CAlive is a complex project. It's actually one part of a much
larger complex project. My goals are not to just create CAlive,
but to create a framework which works for CAlive, and then allows
me and others to rapidly port other languages to it, with all of
them inheriting the same benefits that will first be seen in
CAlive.

> But there are ALWAYS people that answer his supposedely "technical" questions...
>
> DO NOT FEED THE TROLLS DAMM IT!

Perhaps I'm not a troll, rude and mean Jacob.

--
Rick C. Hodgin

Keith Thompson

unread,
Sep 19, 2018, 4:56:17 PM9/19/18
to
Mr Flibble <flibbleREM...@i42.co.uk> writes:
> On 19/09/2018 18:53, Rick C. Hodgin wrote:
[SNIP]
> Nobody is interested in this fucktarded idea of yours mate so give it a rest.
>
> /Flibble

I'm personally not interested in seeing anything Rick posts.
Fortunately, your insistence on quoting his posts so you can complain
about them will no longer be a problem for me.

*plonk*

David Brown

unread,
Sep 19, 2018, 5:24:07 PM9/19/18
to
On 19/09/18 19:43, bitrex wrote:
> On 09/19/2018 12:23 PM, Scott wrote:
>> On Wed, 19 Sep 2018 11:01:45 -0400, "Rick C. Hodgin"
>> <rick.c...@gmail.com> wrote:
>>
>>> Can anyone think of a reason why this type of operation shouldn't
>>> be a valid syntax in a (C/C++)-like lanuage, added to that new
>>> language with these features:
>>>
>>>      if (a <= b < c)
>>>          printf("b is [a, c)\n");
>>>
>>>      if (a < b <= c)
>>>          printf("b is (a, c]\n");
>>>
>>>      if (a < b < c)
>>>          printf("b is (a, c)\n");
>>>
>>> And even these:
>>>
>>>      if (a < b < c < d)
>>>          prinf("b is (a, c)\nc is (b, d)\n");
>>>
>>>      if (a < b < c < d < e)
>>>          prinf("b is (a, c)\nc is (b, d)\nd is (c, e)\n");
>> ...
>>
>> What nonsense are you trolling about now? Everybody knows that these
>> are already perfectly valid constructs in C with simple and
>> unambiguous semantics.
>>
>
> it's ambiguous enough in that gcc emits a warning:

gcc does not give a warning because it is ambiguous - the code is not
ambiguous in C. gcc gives a warning because it is probably incorrect code.

There is nothing wrong with the idea of making expressions like these
match their mathematical meaning. But it would be confusing to do so in
a C-like language.

bitrex

unread,
Sep 19, 2018, 7:48:08 PM9/19/18
to
Yes, I meant "ambiguous" in the sense of my brain might not have been
able to immediately intuit what behavior that form of expression
invoked. Given that it compiled and it does not appear to invoke any
undefined behavior I know of then surely the compiler is not confused.
It will do something, whatever it is.

Alf P. Steinbach

unread,
Sep 19, 2018, 8:50:32 PM9/19/18
to
That's beside the point.

Python provides the mathematical meaning, so it's quite doable.

It's not there in C++ because the developers of original C were figuring
out the syntax and semantics as they went. E.g. the notation for boolean
operations changed on the way. The first set of choices that worked
reasonably well, that was Good Enough, stuck, a case of frozen history.


Cheers & hth.,

- Alf


James Kuyper

unread,
Sep 19, 2018, 9:51:14 PM9/19/18
to
On 09/19/2018 07:47 PM, bitrex wrote:
> On 09/19/2018 05:23 PM, David Brown wrote:
>> On 19/09/18 19:43, bitrex wrote:
>>> On 09/19/2018 12:23 PM, Scott wrote:
>>>> On Wed, 19 Sep 2018 11:01:45 -0400, "Rick C. Hodgin"
>>>> <rick.c...@gmail.com> wrote:
...
>>>>>      if (a < b < c < d < e)
...
>>>> What nonsense are you trolling about now? Everybody knows that these
>>>> are already perfectly valid constructs in C with simple and
>>>> unambiguous semantics.
>>>>
>>>
>>> it's ambiguous enough in that gcc emits a warning:
>>
>> gcc does not give a warning because it is ambiguous - the code is not
>> ambiguous in C.  gcc gives a warning because it is probably incorrect code.
>>
>> There is nothing wrong with the idea of making expressions like these
>> match their mathematical meaning.  But it would be confusing to do so in
>> a C-like language.
>
> Yes, I meant "ambiguous" in the sense of my brain might not have been
> able to immediately intuit what behavior that form of expression
> invoked. Given that it compiled and it does not appear to invoke any
> undefined behavior I know of then surely the compiler is not confused.
> It will do something, whatever it is.

In C, the comparison operators return 1 if the comparison is true, and 0
if it is false. Keeping that in mind, the above code is equivalent to

int i = a < b;
int j = i < c;
int k = j < d;
if(k < e)
...

Does that help?

Ben Bacarisse

unread,
Sep 19, 2018, 11:16:05 PM9/19/18
to
"Alf P. Steinbach" <alf.p.stein...@gmail.com> writes:

> On 19.09.2018 18:23, Scott wrote:
>> On Wed, 19 Sep 2018 11:01:45 -0400, "Rick C. Hodgin"
>> <rick.c...@gmail.com> wrote:
>>
>>> Can anyone think of a reason why this type of operation shouldn't
>>> be a valid syntax in a (C/C++)-like lanuage, added to that new
>>> language with these features:
>>>
>>> if (a <= b < c)
>>> printf("b is [a, c)\n");
<snip>
>> What nonsense are you trolling about now? Everybody knows that these
>> are already perfectly valid constructs in C with simple and
>> unambiguous semantics.
>
> That's beside the point.
>
> Python provides the mathematical meaning, so it's quite doable.
>
> It's not there in C++ because the developers of original C were
> figuring out the syntax and semantics as they went. E.g. the notation
> for boolean operations changed on the way. The first set of choices
> that worked reasonably well, that was Good Enough, stuck, a case of
> frozen history.

Sure, but there's a bit more to it than that. C cites B and BCPL as
inspiration and BCPL has relational operators that chain like this (as
does its more modern incarnation, MCPL). They could have been in C (and
therefore probably in C++) but they missed the cut.

--
Ben.

Öö Tiib

unread,
Sep 20, 2018, 12:11:33 AM9/20/18
to
In C++ we can make something like M(a) < M(b) < M(c) < M(d) to
have mathematical meaning of a < b < c < d by overloading operator<
of M.


Christian Gollwitzer

unread,
Sep 20, 2018, 1:17:45 AM9/20/18
to
Am 20.09.18 um 06:11 schrieb Öö Tiib:
> In C++ we can make something like M(a) < M(b) < M(c) < M(d) to
> have mathematical meaning of a < b < c < d by overloading operator<
> of M.

How would you do that? It seems quite complicated to me, because the
expression is still parsed as ((a < b) < c).

Would you return a proxy object from the comparison which decays to
bool? Then probably you would need to store "b" in this proxy in order
to compare with "c".

Christian

David Brown

unread,
Sep 20, 2018, 2:21:36 AM9/20/18
to
Ah, you mean /your/ interpretation of it is ambiguous - not that the C
itself is ambiguous. Yes, I can appreciate that. There is nothing for
it but to learn the way C interprets this type of expression - and then,
like all sane C programmers, avoid writing it.

bitrex

unread,
Sep 20, 2018, 2:41:11 AM9/20/18
to
you can do a three-way compare in one line without using the &&
operator like:

bool three(unsigned char a, unsigned char b, unsigned char c)
{
return (a < (b < c ? b : false));
}

four way:

bool four_way(unsigned char a, unsigned char b, unsigned char c,
unsigned char d)
return (a < (b < (c < d ? c : false) ? b : false));
}

clang-trunk output of the above, -std=c++11 -O1

cmp dl, cl
jb .LBB0_2
xor edx, edx
.LBB0_2:
cmp dl, sil
ja .LBB0_4
xor esi, esi
.LBB0_4:
cmp sil, dil
seta al
ret

vs:

bool four_way_using_and(unsigned char a, unsigned char b, unsigned char
c, unsigned char d) {
return (a < b && b < c && c < d);
}

output:

cmp dil, sil
setb al
cmp sil, dl
setb sil
and sil, al
cmp dl, cl
setb al
and al, sil
ret



Which using C++1x syntax maybe suggests some kind of template/variadic
recursive-stuff approach for generating implementation for arbitrary
number of arguments of arbitrary type (not tested...)

template <typename T>
auto base(T a) -> decltype(a)
{
return a;
}

template <typename T>
auto two(T a, T b) -> decltype(base(base(a) < base(b) ? base(a) : false))
{
return base(base(a) < base(b) ? base(a) : false);
}

template <typename T>
auto three(T a, T b, T c) ->
decltype(base(base(a) < two(base(b), base(c))))
{
return base(base(a) < two(base(b), base(c)));
}

etc...??


Alf P. Steinbach

unread,
Sep 20, 2018, 4:38:47 AM9/20/18
to
On 20.09.2018 07:17, Christian Gollwitzer wrote:
> Am 20.09.18 um 06:11 schrieb Öö Tiib:
>> In C++ we can make something like M(a) < M(b) < M(c) < M(d) to
>> have mathematical meaning of a < b < c < d by overloading operator<
>> of M.
>
> How would you do that? It seems quite complicated to me, because the
> expression is still parsed as ((a < b) < c).
>
> Would you return a proxy object from the comparison which decays to
> bool?

Yes.

> Then probably you would need to store "b" in this proxy in order
> to compare with "c".

Yes.

But it's IMHO ugly, but code-wise and in resulting notation:


namespace tag
{
struct Math{};
constexpr auto math = Math();
}

template< class Arg >
class Comparison_result
{
Arg m_arg;
bool m_result;

public:
auto result() const -> bool { return m_result; }
explicit operator bool() { return m_result; }

template< class Rhs >
friend auto operator<( const Comparison_result lhs, const Rhs rhs )
-> Comparison_result<Rhs>
{ return Comparison_result<Rhs>( rhs, lhs.m_result and lhs.m_arg <
rhs ); }

Comparison_result( const Arg value, const bool result = true ):
m_arg( value ),
m_result( result )
{}
};

template< class Rhs >
auto operator>>( tag::Math, const Rhs value )
-> Comparison_result<Rhs>
{ return Comparison_result<Rhs>( value ); }

#include <iostream>
using namespace std;

auto main()
-> int
{
double const x = 3.14;
cout << boolalpha;
cout << "In 1...7? " << !!(tag::math>> 1 < x < 7) << endl;
cout << "In 4...7? " << !!(tag::math>> 4 < x < 7) << endl;
}


Cheers!,

- Alf

Öö Tiib

unread,
Sep 20, 2018, 4:43:19 AM9/20/18
to
Yes. For example M < M returns M::R and M::R < M returns also M::R
and only that M::R has conversion to bool.

What can't be done in C++ is short-circuiting that a < b && b < c has.
To achieve LISP-like lazy evaluation effects we have to use inelegant
constructs in C++.

David Brown

unread,
Sep 20, 2018, 5:00:48 AM9/20/18
to
I like the idea of the "tag::math>>" to indicate that you are changing
to a special type of expression mode. It is, IMHO, nicer than the
suggested "M(1) < M(x) < M(7)" or suchlike.

The principle could be used for other cases where you want to use
alternative semantics, like:

(saturating>> x + 1)



Alf P. Steinbach

unread,
Sep 20, 2018, 5:33:33 AM9/20/18
to
On 20.09.2018 11:00, David Brown wrote:
> On 20/09/18 10:38, Alf P. Steinbach wrote:
> [snip]
>> cout << "In 4...7? " << !!(tag::math>> 4 < x < 7) << endl;
>> }
>
> I like the idea of the "tag::math>>" to indicate that you are changing
> to a special type of expression mode. It is, IMHO, nicer than the
> suggested "M(1) < M(x) < M(7)" or suchlike.
>
> The principle could be used for other cases where you want to use
> alternative semantics, like:
>
> (saturating>> x + 1)

The operator precedence works out badly. The above would be parsed as
`saturating >> (x + 1)`. Might do `saturating*` instead, maybe.

The general idea is used by some (or some have claimed to use it) for
pseudo-operators like `a %exp% b`, which is parsed as `(a % exp) % b`.


Cheers!,

- Alf

Bo Persson

unread,
Sep 20, 2018, 6:14:03 AM9/20/18
to
On 2018-09-19 21:22, Mr Flibble wrote:
> On 19/09/2018 18:53, Rick C. Hodgin wrote:
>> On 9/19/2018 1:32 PM, Rick C. Hodgin wrote:
>>> I would also probably place a constraint on it that they be
>>> either stand-alone in an expression, or enclosed with paren-
>>> thesis.
>>
>>
>> I could define it to require braces, so it would then also
>> work with C/C++ because they could add those features as
>> well:
>>
>>      if ({a < b < c})
>>          printf("b is (a,c)\n");
>>
>>      if ({a < b < c < d} || a+b+c+d == 0)
>>          printf("...\n");
>>
>> That might work.
>
> Nobody is interested in this fucktarded idea of yours mate so give it a
> rest.
>

Some ARE actually.

See "Chaining Comparisons" (Sutter and Revzin)

https://wg21.link/p0893


Bo Persson

David Brown

unread,
Sep 20, 2018, 6:43:13 AM9/20/18
to
On 20/09/18 11:33, Alf P. Steinbach wrote:
> On 20.09.2018 11:00, David Brown wrote:
>> On 20/09/18 10:38, Alf P. Steinbach wrote:
>> [snip]
>>> cout << "In 4...7? " << !!(tag::math>> 4 < x < 7) << endl;
>>> }
>>
>> I like the idea of the "tag::math>>" to indicate that you are changing
>> to a special type of expression mode. It is, IMHO, nicer than the
>> suggested "M(1) < M(x) < M(7)" or suchlike.
>>
>> The principle could be used for other cases where you want to use
>> alternative semantics, like:
>>
>> (saturating>> x + 1)
>
> The operator precedence works out badly. The above would be parsed as
> `saturating >> (x + 1)`. Might do `saturating*` instead, maybe.
>

You are right. It's a pity - "saturating>>" had a nice look to it. It
would have to be "saturating*", "saturating%" or "saturating/" to have
the right precedence, and even then it would fail for increment and
decrement.

> The general idea is used by some (or some have claimed to use it) for
> pseudo-operators like `a %exp% b`, which is parsed as `(a % exp) % b`.
>

I can certainly claim to have made such pseudo-operators as examples and
seen that it all works out - but I haven't used them in actual code.
With utf identifiers you can then do:

#define ↑ %exp%

so that you can write "a ↑ b" for a power operator. But that only works
if your compiler is happy with utf identifiers, your editor is happy,
and you are happy with the serious restrictions on which operator
symbols are allowed as "letters" in C++.



Bart

unread,
Sep 20, 2018, 6:50:28 AM9/20/18
to

[Posting separately to c.l.c and c.l.c++, newsreader not supporting
cross-posting.]
That link brings up the interesting point that in C and C++, == and !=
have a different precedence from <, <=, >= and >.

It would make for an odd grouping within such comparison chains.

I don't think I've ever considered that aspect, probably few have,
although many will know that C started off with a ridiculously large
number of precedences.

The rest of the document demonstrates the difficulties in implementing
such a feature in a language such as C++.

I couldn't help checking my implementation of chained operators, and it
was only 25 lines of code. (Although, not for C or C++, and taking the
simpler option of evaluating middle terms twice.)

--
bart

Öö Tiib

unread,
Sep 20, 2018, 6:52:03 AM9/20/18
to
May be I misunderstand but typically the custom numeric types are
converted from dirty data like "Num(x) + n". There x and n are
dirty data and so checks are made and exceptions thrown on
violations. Your alternative schema described above is confusing for
reasons of precedence. The operator% can be used instead of
operator>> but it can be still confusing.

David Brown

unread,
Sep 20, 2018, 7:29:10 AM9/20/18
to
Yes, you need something like % instead of >> for precedence. After
that, "confusing" is in the eye of the beholder. To me, something like
"Sat(x) + n" means "treat x as a saturating integer and add n to it",
while "saturating% x + n" means "calculate x + n using saturating
arithmetic".

But I have not tried either method in real code, so I have no experience
for judging what works best in practice.


Rick C. Hodgin

unread,
Sep 20, 2018, 8:29:15 AM9/20/18
to
On 9/20/2018 6:13 AM, Bo Persson wrote:
> On 2018-09-19 21:22, Mr Flibble wrote:
>> On 19/09/2018 18:53, Rick C. Hodgin wrote:
>>> On 9/19/2018 1:32 PM, Rick C. Hodgin wrote:
>>>> I would also probably place a constraint on it that they be
>>>> either stand-alone in an expression, or enclosed with paren-
>>>> thesis.
>>>
>>> I could define it to require braces, so it would then also
>>> work with C/C++ because they could add those features as
>>> well:
>>>
>>>      if ({a < b < c})
>>>          printf("b is (a,c)\n");
>>>
>>>      if ({a < b < c < d} || a+b+c+d == 0)
>>>          printf("...\n");
>>>
>>> That might work.
>>
>> Nobody is interested in this .. idea of yours mate so give it a rest.
>
> Some ARE actually.
> See "Chaining Comparisons" (Sutter and Revzin)
> https://wg21.link/p0893
>     Bo Persson

+1 ... from April, 2018. Awesome.

--
Rick C. Hodgin

Öö Tiib

unread,
Sep 20, 2018, 8:48:25 AM9/20/18
to
Sure it is always what people are used to, but lets try as thought
experiment. There is one of those two lines:

auto s1 = Saturating(x) + n;
auto s2 = saturating% x + n;

Lets imagine that next maintainer feels some uncertainty what is
going on. What does he do on either case? What is easier? Why?
Lets imagine that there are no much documentation.
Now lets imagine that we are asked to document the class Saturating
or the object saturating. What would you prefer? Why?

Rosario19

unread,
Sep 21, 2018, 6:36:18 AM9/21/18
to
On Wed, 19 Sep 2018 11:01:45 -0400, "Rick C. Hodgin" wrote:

>Can anyone think of a reason why this type of operation shouldn't
>be a valid syntax in a (C/C++)-like lanuage, added to that new
>language with these features:
>
> if (a <= b < c)
> printf("b is [a, c)\n");
>
> if (a < b <= c)
> printf("b is (a, c]\n");
>
> if (a < b < c)
> printf("b is (a, c)\n");
>
>And even these:
>
> if (a < b < c < d)
> prinf("b is (a, c)\nc is (b, d)\n");
>
> if (a < b < c < d < e)
> prinf("b is (a, c)\nc is (b, d)\nd is (c, e)\n");
>
>Et cetera... Each evaluated left-to-right.

if your target is the "more short" the answer would be (if the
language is free, free of use) APL or some other language the people
in codegolf site use

Siri Cruise

unread,
Sep 21, 2018, 7:03:14 AM9/21/18
to
> >Can anyone think of a reason why this type of operation shouldn't
> >be a valid syntax in a (C/C++)-like lanuage, added to that new
> >language with these features:

Proposed: an extension of the if statement:

if (arithmetic-expression) negative-label, zero-label, positive-label

which is equivalent to

typeof(arithmetic-expression) T' = (arithmetic-expression);
if (T'<0) goto negative-label;
if (T'=0) goto zero-label;
if (T'>0) goto positive-label;

This has the obvious extension

if (exp, exp)
neg-neg-label, neg-zero-label, neg-pos-label,
zero-neg-label, zero-zero-label, zero-pos-label,
pos-neg-label, pos-zero-label, pos-pos-label


(The more things change, the more they stay the same.)

--
:-<> Siri Seal of Disavowal #000-001. Disavowed. Denied. Deleted. @
'I desire mercy, not sacrifice.' /|\
An almond doesn't lactate. This post / \
Yet another supercilious snowflake for justice. insults Islam. Mohammed

David Brown

unread,
Sep 21, 2018, 7:19:32 AM9/21/18
to
On 21/09/18 13:02, Siri Cruise wrote:
>>> Can anyone think of a reason why this type of operation shouldn't
>>> be a valid syntax in a (C/C++)-like lanuage, added to that new
>>> language with these features:
>
> Proposed: an extension of the if statement:
>
> if (arithmetic-expression) negative-label, zero-label, positive-label
>
> which is equivalent to
>
> typeof(arithmetic-expression) T' = (arithmetic-expression);
> if (T'<0) goto negative-label;
> if (T'=0) goto zero-label;
> if (T'>0) goto positive-label;
>
> This has the obvious extension
>
> if (exp, exp)
> neg-neg-label, neg-zero-label, neg-pos-label,
> zero-neg-label, zero-zero-label, zero-pos-label,
> pos-neg-label, pos-zero-label, pos-pos-label
>
>
> (The more things change, the more they stay the same.)
>

You forgot the smiley. Either this is a joke and needs a happy smiley,
or it is /seriously/ bad and needs a very unhappy smiley.

bitrex

unread,
Sep 21, 2018, 10:19:36 AM9/21/18
to
I've been writing C/C++ off and on since ALL THE WAY BACK in the late
1990s and I can't say it ever occurred to me to write a test like if ( a
< b < c ) I'd probably just write (a < b && b < c) by force of habit.
the former is very high-school math-y vs. Boolean logic.

with modern C++ I find myself using the "regular" C constructs less and
less and start seeing stuff like multi-way comparisons, nested for
loops, switch statements etc. as possibly more indicative of bad design
then wishing for new operators. I can't think immediately off the top of
my head of a situation where I'd need a four or five way comparison but
now that I have said that it will happen early next week sometime.

Rick C. Hodgin

unread,
Sep 21, 2018, 10:35:37 AM9/21/18
to
On 9/21/2018 10:19 AM, bitrex wrote:
> with modern C++ I find myself using the "regular" C constructs less and less

One of CAlive's goals is to bring some of C++'s better features to
C, but to not bring in the whole gambit.

My goal is to make CAlive be C-like programming with some basic C++
extensions to make syntax easier, and to offer encapsulation.

I prefer:

object->function(...);

Over:

function(object, ...)

Though in CAlive, whether it's a pointer to an object, or an object
directly, you can use the dot syntax:

object.function(...);

--
Rick C. Hodgin

Bart

unread,
Sep 21, 2018, 10:58:01 AM9/21/18
to
On 21/09/2018 15:19, bitrex wrote:

> I've been writing C/C++ off and on since ALL THE WAY BACK in the late
> 1990s and I can't say it ever occurred to me to write a test like if ( a
> < b < c ) I'd probably just write (a < b && b < c) by force of habit.
> the former is very high-school math-y vs. Boolean logic.
>
> with modern C++ I find myself using the "regular" C constructs less and
> less and start seeing stuff like multi-way comparisons, nested for
> loops, switch statements etc. as possibly more indicative of bad design

I'd think the same when I see a lack of them.

(What would the alternative to nested for-loops be I wonder? Or switch
statements? Or multi-way comparisons, whatever you mean by that. Are we
still allowed 'if' or is that also bad design?

I'd be interested in seeing what your code looks like when most control
flow statement are eliminated. I'd imagine it would have either have a
functional look, or perhaps it's just full of C++-ese.)

> then wishing for new operators. I can't think immediately off the top of
> my head of a situation where I'd need a four or five way comparison but
> now that I have said that it will happen early next week sometime.

Here's one actual example of mine (syntax not from C or C++, so "="
means "=="), which is a 4-way compare:

if hd.hsample[2] = hd.vsample[2] = hd.hsample[3] = hd.vsample[3] ...

A simpler one:

if xt = yt = treal ...

Surely everyone at some point must have needed to write the equivalent of:

if 'A' <= c <= 'Z' ...

(Although I would now write that particular form, for integer values, as:

if c in 'A'..'Z' ...)


These look like no-brainers to me. What objections could anyone possibly
have, other than their favourite language doesn't have such constructs?

--
bart

Rick C. Hodgin

unread,
Sep 21, 2018, 11:17:46 AM9/21/18
to
On 9/21/2018 10:57 AM, Bart wrote:
> A simpler one:
>  if xt = yt = treal ...
>
> Surely everyone at some point must have needed to write the equivalent of:
>  if 'A' <= c <= 'Z' ...
>
> (Although I would now write that particular form, for integer values, as:
>  if c in 'A'..'Z' ...)
>
> These look like no-brainers to me. What objections could anyone possibly
> have, other than their favourite language doesn't have such constructs?

I can think of three off the top of my head:

if (xt = yt = treal) ...
if ('A' <= c <= 'Z') ...
if (c in 'A'..'Z') ... // I like this one

In CAlive, I use this:

#define between(v,l,h) (c >= l && c <= h)
if (between(c, 'A', 'Z')) ...

It would be nice to have:

if ('A' < c < 'Z') ...

Though I would like this too, and may add it:

if (c in {'A'..'Z', '0'..'9', 'a'..'z'}) ...

And simplified:

if (c in {a_z, 0_9, a_z}) ...

We'll see.

--
Rick C. Hodgin

Bart

unread,
Sep 21, 2018, 12:28:41 PM9/21/18
to
On 21/09/2018 16:17, Rick C. Hodgin wrote:
> On 9/21/2018 10:57 AM, Bart wrote:
>> A simpler one:
>>   if xt = yt = treal ...
>>
>> Surely everyone at some point must have needed to write the equivalent
>> of:
>>   if 'A' <= c <= 'Z' ...
>>
>> (Although I would now write that particular form, for integer values, as:
>>   if c in 'A'..'Z' ...)
>>
>> These look like no-brainers to me. What objections could anyone
>> possibly have, other than their favourite language doesn't have such
>> constructs?
>
> I can think of three off the top of my head:

Objections?

>     if (xt = yt = treal) ...

In C-style that would need to be == not ==

>     if ('A' <= c <= 'Z') ...
>     if (c in 'A'..'Z') ...      // I like this one
>
> In CAlive, I use this:
>
>     #define between(v,l,h) (c >= l && c <= h)
>     if (between(c, 'A', 'Z')) ...
>
> It would be nice to have:
>
>     if ('A' < c < 'Z') ...
>
> Though I would like this too, and may add it:
>
>     if (c in {'A'..'Z', '0'..'9', 'a'..'z'}) ...
>
> And simplified:
>
>     if (c in {a_z, 0_9, a_z}) ...

(I'm introducing simple forms of Range and Set constructs, from my
dynamic language where they are first-class types. (Both originally from
Pascal; the Set is a bit-set not the complicated container type you find
these days.)

But they will not be types of their own.

The Set construct is used to build 32-, 64- or 128-bit integer values.
That limits it to 128 elements (compared with a billion in the other
language), but that is enough to deal with ASCII codes.

const alpha = ['A'..'Z', 'a'..'z'] # value is 128-bit int
const digits = ['0'..'9']
const ident = alpha ior digits ior ['_'] # ior means "|"

if c in alpha ...
if n in [1,2,4,8] ...
if c not in ident ...

The Range construct is used for array bounds, as switch-case ranges (gcc
has these as 'A'...'Z'), to build Sets, and for tests like this as I've
already shown:

if a in 0..255 ...

Full treatment of such things I don't think belong in a C-class language
but cut-down versions as above I believe are useful, and fairly easy to
implement. They directly translate to simple integer and bit wise
operations.)

--
bart

james...@alumni.caltech.edu

unread,
Sep 21, 2018, 12:44:00 PM9/21/18
to
> < b < c ) ...

Well, of course not - (a < b) < c is a pretty unusual thing to want to calculate. In principle, it could happen, and the amount of C code that
has been written is so great that it's almost certainly the case that
someone, somewhere, has deliberately written a < b < c, knowing that it
would be interpreted as (a < b) < c, because that's what they actually
wanted to calculate. But it's certainly extremely rare.

> ... I'd probably just write (a < b && b < c) by force of habit.

And the fact that you'd want to write that implies that you didn't want
to calculate (a < b) < c.

My objection to this proposal is not based upon the huge quantities of
existing code that would be broken by it - such breakage would be
extremely rare.
My objection is based upon the fact that it is inconsistent with the
design of the rest of the language. a < b is a perfectly good expression
in it's own right which returns a value of type int. It's behavior in
the proposed a < b < c can NOT be describe in terms of the second '<'
operating on the value returned by a < b, which makes it very different
from any other type of C expression.

The only other expression types that surround an operand with tokens are
?: and various expression types that use [], (), or {} to surround the
operand. Since none of the members of those pairs can appear on their
own, you never get a case where part of the entire expression looks
like an expression in it's own right, but isn't treated as one. There's
no possibility, in any of the following expressions, that the part
extending from the beginning up to b will be interpretable as an
expression in it's own right. The same is true of the part starting at
'b' and extending to the right:

a ? b : c
a[b]
a(b)
(a){b, c}
(a)b
_Generic(a, int:b, float:c)

Rick C. Hodgin

unread,
Sep 21, 2018, 12:44:03 PM9/21/18
to
On 9/21/2018 12:28 PM, Bart wrote:
> On 21/09/2018 16:17, Rick C. Hodgin wrote:
>> On 9/21/2018 10:57 AM, Bart wrote:
>>> A simpler one:
>>>   if xt = yt = treal ...
>>>
>>> Surely everyone at some point must have needed to write the equivalent of:
>>>   if 'A' <= c <= 'Z' ...
>>>
>>> (Although I would now write that particular form, for integer values, as:
>>>   if c in 'A'..'Z' ...)
>>>
>>> These look like no-brainers to me. What objections could anyone possibly
>>> have, other than their favourite language doesn't have such constructs?
>>
>> I can think of three off the top of my head:
>
> Objections?

I was being silly. I simply added parenthesis around yours.

>>      if (xt = yt = treal) ...
>
> In C-style that would need to be == not ==

I just copied what you wrote. I didn't even pay that much
attention to what it was because I was just adding the paren-
thesis..

>>      if ('A' <= c <= 'Z') ...
>>      if (c in 'A'..'Z') ...      // I like this one
>>
>> In CAlive, I use this:
>>
>>      #define between(v,l,h) (c >= l && c <= h)
>>      if (between(c, 'A', 'Z')) ...
>>
>> It would be nice to have:
>>
>>      if ('A' < c < 'Z') ...
>>
>> Though I would like this too, and may add it:
>>
>>      if (c in {'A'..'Z', '0'..'9', 'a'..'z'}) ...
>>
>> And simplified:
>>
>>      if (c in {a_z, 0_9, a_z}) ...
>
> (I'm introducing simple forms of Range and Set constructs, from my dynamic
> language where they are first-class types. (Both originally from Pascal; the
> Set is a bit-set not the complicated container type you find these days.)

I remember those. I've considered how to implement them in
CAlive and came up with [|definition|] casks that allow for
[|min|value||], [|max|value||], [|range|min,max||].

> But they will not be types of their own.

In CAlive, they are not enforced, but they signal an exception
for <|range|func()||> to be added wherever needed on any oper-
ation.

> The Set construct is used to build 32-, 64- or 128-bit integer values. That
> limits it to 128 elements (compared with a billion in the other language),
> but that is enough to deal with ASCII codes.
>
>   const alpha = ['A'..'Z', 'a'..'z']         # value is 128-bit int
>   const digits = ['0'..'9']
>   const ident  = alpha ior digits ior ['_']  # ior means "|"
>
>   if c in alpha ...
>   if n in [1,2,4,8] ...
>   if c not in ident ...

Interesting.

> The Range construct is used for array bounds, as switch-case ranges (gcc has
> these as 'A'...'Z'), to build Sets, and for tests like this as I've already
> shown:
>
>   if a in 0..255 ...
>
> Full treatment of such things I don't think belong in a C-class language but
> cut-down versions as above I believe are useful, and fairly easy to
> implement. They directly translate to simple integer and bit wise operations.)

I really admire your unique and creative approach toward language
extensions. I enjoy reading them in the various posts.

--
Rick C. Hodgin

bitrex

unread,
Sep 21, 2018, 12:49:38 PM9/21/18
to
On 09/21/2018 10:57 AM, Bart wrote:
> On 21/09/2018 15:19, bitrex wrote:
>
>> I've been writing C/C++ off and on since ALL THE WAY BACK in the late
>> 1990s and I can't say it ever occurred to me to write a test like if (
>> a < b < c ) I'd probably just write (a < b && b < c) by force of
>> habit. the former is very high-school math-y vs. Boolean logic.
>>
>> with modern C++ I find myself using the "regular" C constructs less
>> and less and start seeing stuff like multi-way comparisons, nested for
>> loops, switch statements etc. as possibly more indicative of bad design
>
> I'd think the same when I see a lack of them.
>
> (What would the alternative to nested for-loops be I wonder? Or switch
> statements? Or multi-way comparisons, whatever you mean by that. Are we
> still allowed 'if' or is that also bad design?

Lots of tasks that require nested loops in C can be accomplished in C++
using the standard algorithms operating on containers, you'd want to use
those whenever possible.

<http://www.cplusplus.com/reference/algorithm/>

> I'd be interested in seeing what your code looks like when most control
> flow statement are eliminated. I'd imagine it would have either have a
> functional look, or perhaps it's just full of C++-ese.)

Writing "C++ese" is what all that stuff is there for! golly!

>> then wishing for new operators. I can't think immediately off the top
>> of my head of a situation where I'd need a four or five way comparison
>> but now that I have said that it will happen early next week sometime.
>
> Here's one actual example of mine (syntax not from C or C++, so "="
> means "=="), which is a 4-way compare:
>
>  if hd.hsample[2] = hd.vsample[2] = hd.hsample[3] = hd.vsample[3] ...

it's an error-prone statement (all copy-paste statements are.) e.g. if
you accidentally write:

if hd.hsample[2] = hd.vsample[2] = hd.hsample[3] = hd.hsample[3]

can someone not familiar see what's different in 5 seconds? if you don't
notice it yourself and the incorrect statement returns a valid result
that compiles OK ya ded and then you (or someone!) tear ya hair out for
four hours trying to find it. Even "Big name" codebases are filled with
bugs like that.

bitrex

unread,
Sep 21, 2018, 1:01:48 PM9/21/18
to
The two-way comparison operators return bool in C++, yeah?

<https://en.cppreference.com/w/cpp/language/operator_comparison>

(looks like this topic was cross-posted so maybe some ambiguity about
what lang we're talking about here.)

in C++ this works fine: if (a < ( b < c ? b : false))

james...@alumni.caltech.edu

unread,
Sep 21, 2018, 1:25:33 PM9/21/18
to
On Friday, September 21, 2018 at 1:01:48 PM UTC-4, bitrex wrote:
> On 09/21/2018 12:43 PM, james...@alumni.caltech.edu wrote:
...
> > My objection is based upon the fact that it is inconsistent with the
> > design of the rest of the language. a < b is a perfectly good expression
> > in it's own right which returns a value of type int. It's behavior in
> > the proposed a < b < c can NOT be describe in terms of the second '<'
> > operating on the value returned by a < b, which makes it very different
> > from any other type of C expression.
>
> The two-way comparison operators return bool in C++, yeah?

True - but the type of the value it returns isn't relevant to the point
I was raising. All that matters is that it does return a value, and that
the proposed new meaning for a < b < c cannot be interpreted as the
second operator operating on the value returned by the first one.
C++ has added a lot of new syntax in the latest release, that I'm not
yet familiar with - the rest of my message might have to be re-written
to cover C++ as well, but I believe that the basic point I'm trying to
make is equally valid for either language.

> <https://en.cppreference.com/w/cpp/language/operator_comparison>
>
> (looks like this topic was cross-posted so maybe some ambiguity about
> what lang we're talking about here.)
>
> in C++ this works fine: if (a < ( b < c ? b : false))

It works, in the sense that it has well-defined behavior so long as a,
b, and c have appropriate types. I'm not sure there's any easy way to
describe what it does. It's certainly not equivalent to either the
current meaning of a < b < c, nor the proposed new meaning for that
expression. Why do you bring it up?

Bart

unread,
Sep 21, 2018, 1:25:43 PM9/21/18
to
On 21/09/2018 17:49, bitrex wrote:
> On 09/21/2018 10:57 AM, Bart wrote:

>> Here's one actual example of mine (syntax not from C or C++, so "="
>> means "=="), which is a 4-way compare:
>>
>>   if hd.hsample[2] = hd.vsample[2] = hd.hsample[3] = hd.vsample[3] ...
>
> it's an error-prone statement (all copy-paste statements are.) e.g. if
> you accidentally write:
>
> if hd.hsample[2] = hd.vsample[2] = hd.hsample[3] = hd.hsample[3]
>
> can someone not familiar see what's different in 5 seconds?

Writing:

if hd.hsample[2]=hd.vsample[2] and hd.vsample[2]=hd.hsample[3] and
hd.hsample[3]=hd.vsample[3] ...

would be even more error-prone! It means writing 6 terms instead of 4.

And it is necessary for the terms either side of the 'and's to match,
which introduces another source of errors when the same term has to be
repeated: if updating one instance, you must update the other too.

(Note that your modified version compares the same term with itself,
which is the sort of thing a compiler might warn about.)


if you don't
> notice it yourself and the incorrect statement returns a valid result
> that compiles OK ya ded and then you (or someone!) tear ya hair out for
> four hours trying to find it. Even "Big name" codebases are filled with
> bugs like that.


Yes. But as I said, using this construct *reduces* the scope for such
errors.

--
bart

Keith Thompson

unread,
Sep 21, 2018, 1:47:11 PM9/21/18
to
Siri Cruise <chine...@yahoo.com> writes:
> Proposed: an extension of the if statement:
>
> if (arithmetic-expression) negative-label, zero-label, positive-label
>
> which is equivalent to
>
> typeof(arithmetic-expression) T' = (arithmetic-expression);
> if (T'<0) goto negative-label;
> if (T'=0) goto zero-label;
> if (T'>0) goto positive-label;
>
> This has the obvious extension
>
> if (exp, exp)
> neg-neg-label, neg-zero-label, neg-pos-label,
> zero-neg-label, zero-zero-label, zero-pos-label,
> pos-neg-label, pos-zero-label, pos-pos-label
>
>
> (The more things change, the more they stay the same.)

C++20 is adding a "three-way comparison operator", <=>, inspired by
Perl's "spaceship operator". It compares two values and yields less,
equal, or greater (of type std::strong_ordering), with some added
complications for floating-point operands. Using it with a switch
statement would be similar to what you (sarcastically, I presume)
propose.

See also
https://github.com/Keith-S-Thompson/fizzbuzz-c/blob/master/fizzbuzz093.c

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Bart

unread,
Sep 21, 2018, 1:55:03 PM9/21/18
to
On 21/09/2018 17:43, james...@alumni.caltech.edu wrote:

[I might be killfilled by this poster, but decided to post anyway.]

> My objection to this proposal is not based upon the huge quantities of
> existing code that would be broken by it - such breakage would be
> extremely rare.
> My objection is based upon the fact that it is inconsistent with the
> design of the rest of the language. a < b is a perfectly good expression
> in it's own right which returns a value of type int. It's behavior in
> the proposed a < b < c can NOT be describe in terms of the second '<'
> operating on the value returned by a < b, which makes it very different
> from any other type of C expression.

If the syntax is converted to s-expressions, then:

A < B < C

using the normal semantics could be expressed like this:

(LT (LT A B) C)

The outer LT operates on the result of the inner LT as you said.

My crude implementation performs a transformation so that you end up with:

(AND (LT A B) (LT B C))

Which works fine, except there's a flaw because B occurs twice here, and
only once in the original source, which is a no-no.

A workaround could create a temporary and copy B into that. But I think
this requires a special construct within the language, denoted by this:

(CHAINCMP
(A LT)
(B LT)
C)

This will always have two or more of those pairs (otherwise it's a
regular compare), ending with the single tail expression.

Then middle terms only appear once, and it's easy to generate the most
suitable code.

(That this would be incompatible with how both C and C++ work now is a
significant detail that shouldn't be overlooked.

Both those languages also have a small problem with the relative
precedences of EQ/NE, compared with LT/LE/GE/GT. For this proposal, they
would really need to be the same.)

--
bart

bitrex

unread,
Sep 21, 2018, 3:10:56 PM9/21/18
to
On 09/21/2018 01:25 PM, Bart wrote:
> On 21/09/2018 17:49, bitrex wrote:
>> On 09/21/2018 10:57 AM, Bart wrote:
>
>>> Here's one actual example of mine (syntax not from C or C++, so "="
>>> means "=="), which is a 4-way compare:
>>>
>>>   if hd.hsample[2] = hd.vsample[2] = hd.hsample[3] = hd.vsample[3] ...
>>
>> it's an error-prone statement (all copy-paste statements are.) e.g. if
>> you accidentally write:
>>
>> if hd.hsample[2] = hd.vsample[2] = hd.hsample[3] = hd.hsample[3]
>>
>> can someone not familiar see what's different in 5 seconds?
>
> Writing:
>
>  if hd.hsample[2]=hd.vsample[2] and hd.vsample[2]=hd.hsample[3] and
>     hd.hsample[3]=hd.vsample[3] ...
>
> would be even more error-prone! It means writing 6 terms instead of 4.

I wouldn't write that either! You would use range-based iterators and
the std library functions that operate on the iterators returned by
containers like std::equal, std::lexicographical_compare, etc.

Siri Cruise

unread,
Sep 21, 2018, 3:12:46 PM9/21/18
to
In article <lnd0t6a...@kst-u.example.com>, Keith Thompson <ks...@mib.org>
wrote:

> Siri Cruise <chine...@yahoo.com> writes:
> > Proposed: an extension of the if statement:
> >
> > if (arithmetic-expression) negative-label, zero-label, positive-label
> >
> > which is equivalent to
> >
> > typeof(arithmetic-expression) T' = (arithmetic-expression);
> > if (T'<0) goto negative-label;
> > if (T'=0) goto zero-label;
> > if (T'>0) goto positive-label;
> >
> > This has the obvious extension
> >
> > if (exp, exp)
> > neg-neg-label, neg-zero-label, neg-pos-label,
> > zero-neg-label, zero-zero-label, zero-pos-label,
> > pos-neg-label, pos-zero-label, pos-pos-label
> >
> >
> > (The more things change, the more they stay the same.)
>
> C++20 is adding a "three-way comparison operator", <=>, inspired by
> Perl's "spaceship operator". It compares two values and yields less,
> equal, or greater (of type std::strong_ordering), with some added
> complications for floating-point operands. Using it with a switch
> statement would be similar to what you (sarcastically, I presume)
> propose.

Doesn't everyone want to program in 1966 Fortran?

bitrex

unread,
Sep 21, 2018, 3:15:01 PM9/21/18
to
On 09/21/2018 01:25 PM, Bart wrote:
in situation where you have say a vector and want to compare elements
prefer to use proxy objects like iterators for comparisons and not like
my_vector[0], my_vector[1], etc.

unless you actually have to destructively alter the contents of the
container don't touch 'em directly, don't do nothin' with 'em!

Öö Tiib

unread,
Sep 21, 2018, 3:34:54 PM9/21/18
to
On Friday, 21 September 2018 22:12:46 UTC+3, Siri Cruise wrote:
> In article <lnd0t6a...@kst-u.example.com>, Keith Thompson <ks...@mib.org>
> wrote:
>
> > Siri Cruise <chine...@yahoo.com> writes:
> > > Proposed: an extension of the if statement:
> > >
> > > if (arithmetic-expression) negative-label, zero-label, positive-label
> > >
> > > which is equivalent to
> > >
> > > typeof(arithmetic-expression) T' = (arithmetic-expression);
> > > if (T'<0) goto negative-label;
> > > if (T'=0) goto zero-label;
> > > if (T'>0) goto positive-label;
> > >
> > > This has the obvious extension
> > >
> > > if (exp, exp)
> > > neg-neg-label, neg-zero-label, neg-pos-label,
> > > zero-neg-label, zero-zero-label, zero-pos-label,
> > > pos-neg-label, pos-zero-label, pos-pos-label
> > >
> > >
> > > (The more things change, the more they stay the same.)
> >
> > C++20 is adding a "three-way comparison operator", <=>, inspired by
> > Perl's "spaceship operator". It compares two values and yields less,
> > equal, or greater (of type std::strong_ordering), with some added
> > complications for floating-point operands. Using it with a switch
> > statement would be similar to what you (sarcastically, I presume)
> > propose.
>
> Doesn't everyone want to program in 1966 Fortran?

The C++ (like always) actually tries to top up the complications
invented in other languages. When the operands of spaceship are
floating point types then the "three-way comparison" will evaluate
to one of four values of std::partial_ordering. ;)

Rick C. Hodgin

unread,
Sep 21, 2018, 3:53:36 PM9/21/18
to
On 9/21/2018 3:12 PM, Siri Cruise wrote:
> Doesn't everyone want to program in 1966 Fortran?

People often make fun of Fortran, but it's still a first-tier
compiler receiving modern optimizations provided for by Intel
(along with their C++ compiler).

https://software.intel.com/en-us/intel-compilers

I regularly hear speakers on compute-related videos report
that their scientists say there isn't a better language to
express the formulas they use for calculation than in Fortran.

I've never used it personally. I play to support it with
RDC though at some point.

--
Rick C. Hodgin

Bart

unread,
Sep 21, 2018, 4:53:46 PM9/21/18
to
On 21/09/2018 20:10, bitrex wrote:
> On 09/21/2018 01:25 PM, Bart wrote:
>> On 21/09/2018 17:49, bitrex wrote:
>>> On 09/21/2018 10:57 AM, Bart wrote:
>>
>>>> Here's one actual example of mine (syntax not from C or C++, so "="
>>>> means "=="), which is a 4-way compare:
>>>>
>>>>   if hd.hsample[2] = hd.vsample[2] = hd.hsample[3] = hd.vsample[3] ...
>>>
>>> it's an error-prone statement (all copy-paste statements are.) e.g.
>>> if you accidentally write:
>>>
>>> if hd.hsample[2] = hd.vsample[2] = hd.hsample[3] = hd.hsample[3]
>>>
>>> can someone not familiar see what's different in 5 seconds?
>>
>> Writing:
>>
>>   if hd.hsample[2]=hd.vsample[2] and hd.vsample[2]=hd.hsample[3] and
>>      hd.hsample[3]=hd.vsample[3] ...
>>
>> would be even more error-prone! It means writing 6 terms instead of 4.
>
> I wouldn't write that either! You would use range-based iterators and
> the std library functions that operate on the iterators returned by
> containers like std::equal, std::lexicographical_compare, etc.

So what would you write? In any syntax.

Forget the "hd." parts, you have:

int hsample[4]; // sampling rate per component
int hsample[4];

When number of components is 3 (eg. RGB), then you want to test that
hsample[1], vsample[1], hsample[2], vsample[2] (using 0-based index) all
have the same value before proceeding.

Is there a simpler and more robust way than using (in C syntax and with
chained compare ops):

if (hsample[1] == vsample[1] == hsample[2] == vsample[2])

?



--
bart

bitrex

unread,
Sep 21, 2018, 4:58:55 PM9/21/18
to
On 09/21/2018 04:53 PM, Bart wrote:
> On 21/09/2018 20:10, bitrex wrote:
>> On 09/21/2018 01:25 PM, Bart wrote:
>>> On 21/09/2018 17:49, bitrex wrote:
>>>> On 09/21/2018 10:57 AM, Bart wrote:
>>>
>>>>> Here's one actual example of mine (syntax not from C or C++, so "="
>>>>> means "=="), which is a 4-way compare:
>>>>>
>>>>>   if hd.hsample[2] = hd.vsample[2] = hd.hsample[3] = hd.vsample[3] ...
>>>>
>>>> it's an error-prone statement (all copy-paste statements are.) e.g.
>>>> if you accidentally write:
>>>>
>>>> if hd.hsample[2] = hd.vsample[2] = hd.hsample[3] = hd.hsample[3]
>>>>
>>>> can someone not familiar see what's different in 5 seconds?
>>>
>>> Writing:
>>>
>>>   if hd.hsample[2]=hd.vsample[2] and hd.vsample[2]=hd.hsample[3] and
>>>      hd.hsample[3]=hd.vsample[3] ...
>>>
>>> would be even more error-prone! It means writing 6 terms instead of 4.
>>
>> I wouldn't write that either! You would use range-based iterators and
>> the std library functions that operate on the iterators returned by
>> containers like std::equal, std::lexicographical_compare, etc.
>
> So what would you write? In any syntax.
>
> Forget the "hd." parts, you have:
>
>    int hsample[4];        // sampling rate per component
>    int hsample[4];

?????? see what I mean I don't trust me either!

Bart

unread,
Sep 21, 2018, 5:28:53 PM9/21/18
to
On 21/09/2018 21:58, bitrex wrote:
> On 09/21/2018 04:53 PM, Bart wrote:

>> So what would you write? In any syntax.
>>
>> Forget the "hd." parts, you have:
>>
>>     int hsample[4];        // sampling rate per component
>>     int hsample[4];
>
> ?????? see what I mean I don't trust me either!

This is a typo that would be picked up, so is of no consequence.
(Obviously one of those will be vsample.)

>> Is there a simpler and more robust way than using (in C syntax and
>> with chained compare ops):
>>
>>     if (hsample[1] == vsample[1] == hsample[2] == vsample[2])

I'm still interested in the magical solution that is simple and less
error-prone than this.

--
bart

Öö Tiib

unread,
Sep 21, 2018, 6:01:05 PM9/21/18
to
No need to make new programming languages for that, just write two
one-liners in C++11 and done. Nothing magical.
Example that will work with operands of any types between what
operator == that returns bool exists.


template<typename T, typename U>
bool allEqual(T&& t, U&& u)
{
return (t == u);
}

template<typename T, typename U, typename... Ts>
bool allEqual(T&& t, U&& u, Ts&&... args)
{
return (t == u) && allEqual(u, std::forward<Ts>(args)...);
}

// done, now you have allEqual(a,b,c,d,...)
// usage:
#include <iostream>

int main()
{
int hsample[4] = {42, 42, 42, 42};
int vsample[4] = {0, 42, 42, 666};
if (allEqual(hsample[1], vsample[1], hsample[2], vsample[2]))
{
std::cout << "yes\n";
}
}


Bart

unread,
Sep 21, 2018, 7:45:19 PM9/21/18
to
On 21/09/2018 23:00, Öö Tiib wrote:
> On Saturday, 22 September 2018 00:28:53 UTC+3, Bart wrote:

>>>>     if (hsample[1] == vsample[1] == hsample[2] == vsample[2])
>>
>> I'm still interested in the magical solution that is simple and less
>> error-prone than this.

> No need to make new programming languages for that, just write two
> one-liners in C++11 and done. Nothing magical.
> Example that will work with operands of any types between what
> operator == that returns bool exists.

Implementing chained "==" would also work with any types for which "=="
is valid. And the same solution would work for "!=", as well as
"<","<=",">=", and ">", although those would be valid for fewer types.

>
> template<typename T, typename U>
> bool allEqual(T&& t, U&& u)
> {
> return (t == u);
> }
>
> template<typename T, typename U, typename... Ts>
> bool allEqual(T&& t, U&& u, Ts&&... args)
> {
> return (t == u) && allEqual(u, std::forward<Ts>(args)...);
> }

(On rextester.com, this only worked with VC++, not gcc or clang where it
didn't like std::forward.

I don't know C++ enough to know what overheads (both compile-time and
run-time) such a solution involves and what disadvantages there are,
apart from having to do it.

And then being stuck with a small support library that now has to be
part of the code.)

> // done, now you have allEqual(a,b,c,d,...)
> // usage:
> #include <iostream>
>
> int main()
> {
> int hsample[4] = {42, 42, 42, 42};
> int vsample[4] = {0, 42, 42, 666};
> if (allEqual(hsample[1], vsample[1], hsample[2], vsample[2]))

I thought the problem was all the errors that could occur when you have
to enumerate all the terms one by one. This doesn't solve that. That
last term can still be written hsample[2].

Remember as written in the language of the original example (using
1-based) it would be:

if hsample[2] = vsample[2] = hsample[3] = vsample[3] then


--
bart

Alf P. Steinbach

unread,
Sep 21, 2018, 8:49:20 PM9/21/18
to
On 22.09.2018 00:00, Öö Tiib wrote:
> On Saturday, 22 September 2018 00:28:53 UTC+3, Bart wrote:
>> On 21/09/2018 21:58, bitrex wrote:
>>> On 09/21/2018 04:53 PM, Bart wrote:
>>>> [snip]
>>>> Is there a simpler and more robust way than using (in C syntax and
>>>> with chained compare ops):
>>>>
>>>>     if (hsample[1] == vsample[1] == hsample[2] == vsample[2])
>>
>> I'm still interested in the magical solution that is simple and less
>> error-prone than this.
>
> No need to make new programming languages for that, just write two
> one-liners in C++11 and done. Nothing magical.
> Example that will work with operands of any types between what
> operator == that returns bool exists.
>
>
> template<typename T, typename U>
> bool allEqual(T&& t, U&& u)
> {
> return (t == u);
> }
>
> template<typename T, typename U, typename... Ts>
> bool allEqual(T&& t, U&& u, Ts&&... args)
> {
> return (t == u) && allEqual(u, std::forward<Ts>(args)...);
> }
>
> // done, now you have allEqual(a,b,c,d,...)
> // usage:
> #include <iostream>
>
> int main()
> {
> int hsample[4] = {42, 42, 42, 42};
> int vsample[4] = {0, 42, 42, 666};
> if (allEqual(hsample[1], vsample[1], hsample[2], vsample[2]))
> {
> std::cout << "yes\n";
> }
> }

Not sure I like the recursion there. Also, I feel it should have
shortcut evaluation, a.k.a. “short-circuit evaluation”. Like,

#include <array> // std::(array)
#include <functional> // std::(reference_wrapper)
#include <type_traits> // std::(common_type_t)

namespace my
{
using std::common_type_t, std::array, std::reference_wrapper;

inline auto all_equal() -> bool { return true; }

template< class... Items >
inline auto all_equal( const Items&... items )
-> bool
{
using Item = common_type_t< Items... >;
constexpr int n = sizeof...( items );

const array<reference_wrapper<const Item>, n> item_refs = {
items... };
for( int i = 1; i < n; ++i )
{
if( item_refs[i] != item_refs[0] ) { return false; }
}
return true;
}
}

#include <iostream>
using namespace std;
auto main()
-> int
{
const int hsample[] = { 42, 42, 42, 42 };
const int vsample[] = { 0, 42, 42, 666 };

if( my::all_equal( hsample[1], vsample[1], hsample[2],
vsample[2] ) )
{
cout << "yes" << endl;
}
}


Cheers!,

- Alf (in a non-recursive mood)

Öö Tiib

unread,
Sep 21, 2018, 9:12:36 PM9/21/18
to
On Saturday, 22 September 2018 02:45:19 UTC+3, Bart wrote:
> On 21/09/2018 23:00, Öö Tiib wrote:
> > On Saturday, 22 September 2018 00:28:53 UTC+3, Bart wrote:
>
> >>>>     if (hsample[1] == vsample[1] == hsample[2] == vsample[2])
> >>
> >> I'm still interested in the magical solution that is simple and less
> >> error-prone than this.
>
> > No need to make new programming languages for that, just write two
> > one-liners in C++11 and done. Nothing magical.
> > Example that will work with operands of any types between what
> > operator == that returns bool exists.
>
> Implementing chained "==" would also work with any types for which "=="
> is valid. And the same solution would work for "!=", as well as
> "<","<=",">=", and ">", although those would be valid for fewer types.
>
> >
> > template<typename T, typename U>
> > bool allEqual(T&& t, U&& u)
> > {
> > return (t == u);
> > }
> >
> > template<typename T, typename U, typename... Ts>
> > bool allEqual(T&& t, U&& u, Ts&&... args)
> > {
> > return (t == u) && allEqual(u, std::forward<Ts>(args)...);
> > }
>
> (On rextester.com, this only worked with VC++, not gcc or clang where it
> didn't like std::forward.

Oh. #include <utility> Also most common C++ headers like <iostream>
will cause it to be included as well.

>
> I don't know C++ enough to know what overheads (both compile-time and
> run-time) such a solution involves and what disadvantages there are,
> apart from having to do it.
>
> And then being stuck with a small support library that now has to be
> part of the code.)
>
> > // done, now you have allEqual(a,b,c,d,...)
> > // usage:
> > #include <iostream>
> >
> > int main()
> > {
> > int hsample[4] = {42, 42, 42, 42};
> > int vsample[4] = {0, 42, 42, 666};
> > if (allEqual(hsample[1], vsample[1], hsample[2], vsample[2]))
>
> I thought the problem was all the errors that could occur when you have
> to enumerate all the terms one by one. This doesn't solve that. That
> last term can still be written hsample[2].

I do not know a tool that can figure out that we wrote something that
is valid but does not mean what we did want to write. We are often
distracted or preoccupied with other things so it happens daily. I just
write unit tests to double-check if my function results with what I did
mean it to result.

> Remember as written in the language of the original example (using
> 1-based) it would be:
>
> if hsample[2] = vsample[2] = hsample[3] = vsample[3] then

It will complain? When you write:

if hsample[2] = vsample[2] = hsample[3] = vsample[2] then

Why?

Öö Tiib

unread,
Sep 21, 2018, 9:47:54 PM9/21/18
to
Is it because of you feel recursion harder to read? Compilers tend to
optimize above program to 'std::cout << "yes\n";' anyway.
Demo: https://godbolt.org/z/s8K7bB
We may need somewhat more tricky input data to see if there are any
disadvantages.
Yes, compilers optimize that to 'std::cout << "yes" << std::endl;'
as well.

Joe Pfeiffer

unread,
Sep 21, 2018, 11:18:06 PM9/21/18
to
"Rick C. Hodgin" <rick.c...@gmail.com> writes:

> On 9/21/2018 3:12 PM, Siri Cruise wrote:
>> Doesn't everyone want to program in 1966 Fortran?
>
> People often make fun of Fortran, but it's still a first-tier
> compiler receiving modern optimizations provided for by Intel
> (along with their C++ compiler).
>
> https://software.intel.com/en-us/intel-compilers
>
> I regularly hear speakers on compute-related videos report
> that their scientists say there isn't a better language to
> express the formulas they use for calculation than in Fortran.

I'm a little surprised they don't prefer Mathematica since it does
*really* nicely with vector and matrix operations. But really, if
you're doing scalar equations Fortran is neither substantially better
nor worse than just about anything else out there. Yeah, a builtin
exponentiation operator is nice, as is builtin complex numbers (which C
also has), but they aren't a big deal.

> I've never used it personally. I play to support it with
> RDC though at some point.

Don't bother. Life is short; there isn't time to learn computer
languages unless there is something you want to use them for (the
closest I ever came to having a real job was being a student intern
at Weyerhaeuser, doing simulations and dynamic programming in Fortran.
It's a language. Nothing really special about it after all these
years).

David Brown

unread,
Sep 22, 2018, 5:12:39 AM9/22/18
to
On 22/09/18 01:45, Bart wrote:
> On 21/09/2018 23:00, Öö Tiib wrote:
>> On Saturday, 22 September 2018 00:28:53 UTC+3, Bart  wrote:
>
>>>>>      if (hsample[1] == vsample[1] == hsample[2] == vsample[2])
>>>
>>> I'm still interested in the magical solution that is simple and less
>>> error-prone than this.
>
>> No need to make new programming languages for that, just write two
>> one-liners in C++11 and done. Nothing magical.
>> Example that will work with operands of any types between what
>> operator == that returns bool exists.
>
> Implementing chained "==" would also work with any types for which "=="
> is valid. And the same solution would work for "!=", as well as
> "<","<=",">=", and ">", although those would be valid for fewer types.
>
>>
>>      template<typename T, typename U>
>>      bool allEqual(T&& t, U&& u)
>>      {
>>          return (t == u);
>>      }
>>
>>      template<typename T, typename U, typename... Ts>
>>      bool allEqual(T&& t, U&& u, Ts&&... args)
>>      {
>>          return (t == u) && allEqual(u, std::forward<Ts>(args)...);
>>      }
>
> (On rextester.com, this only worked with VC++, not gcc or clang where it
> didn't like std::forward.
>
> I don't know C++ enough to know what overheads (both compile-time and
> run-time) such a solution involves and what disadvantages there are,
> apart from having to do it.

These two give identical code with gcc:

bool check1(void) {
return hsample[1] == vsample[1] &&
vsample[1] == hsample[2] &&
hsample[2] == vsample[2];
}

bool check2(void) {
return allEqual(hsample[1], vsample[1], hsample[2], vsample[2]);
}

So no runtime overhead (as expected).

Templates like this don't normally have any run-time costs - nor do
things like std::forward or std::move that are just odd (for non C++
gurus) type manipulations. C++ uses some of that sort of function that
don't seem to actually /do/ anything, just pass data in and out with
different kinds of references or type variations added or removed. They
are mainly so that the right function gets called in the end, while
still retaining the type safety and type information.

David Brown

unread,
Sep 22, 2018, 5:25:14 AM9/22/18
to
PVS Studio (apparently) does a good of spotting copy-and-paste errors,
such as writing hsample[2] instead of vsample[2] at the end of the list.

For those on a lesser budget, gcc "-Wduplicated-cond" and
"-Wduplicated-branches" can sometimes help, but don't trigger here.

David Brown

unread,
Sep 22, 2018, 5:32:51 AM9/22/18
to
On 21/09/18 19:46, Keith Thompson wrote:
> Siri Cruise <chine...@yahoo.com> writes:
>> Proposed: an extension of the if statement:
>>
>> if (arithmetic-expression) negative-label, zero-label, positive-label
>>
>> which is equivalent to
>>
>> typeof(arithmetic-expression) T' = (arithmetic-expression);
>> if (T'<0) goto negative-label;
>> if (T'=0) goto zero-label;
>> if (T'>0) goto positive-label;
>>
>> This has the obvious extension
>>
>> if (exp, exp)
>> neg-neg-label, neg-zero-label, neg-pos-label,
>> zero-neg-label, zero-zero-label, zero-pos-label,
>> pos-neg-label, pos-zero-label, pos-pos-label
>>
>>
>> (The more things change, the more they stay the same.)
>
> C++20 is adding a "three-way comparison operator", <=>, inspired by
> Perl's "spaceship operator". It compares two values and yields less,
> equal, or greater (of type std::strong_ordering), with some added
> complications for floating-point operands. Using it with a switch
> statement would be similar to what you (sarcastically, I presume)
> propose.
>
> See also
> https://github.com/Keith-S-Thompson/fizzbuzz-c/blob/master/fizzbuzz093.c
>

The spaceship operator is not expected to find much use in user code
like this. The main idea is that if you want to make a class for
objects with comparison, you can define a single spaceship operator
function for it and get <, >, ==, !=, <= and >= generated automatically.
It is a labour (and therefore bug) saving device, rather than for
application code.

Of course, that doesn't mean you can't use it in switches like this.

Öö Tiib

unread,
Sep 22, 2018, 8:12:32 AM9/22/18
to
That is certainly one usage. But there can be others. I see two more
but haven't yet thought out proper policies:

1) Until spaceship checking incomparability is painful.

if (!(a == b) && !(a != b)) // anyone?

With spaceship it will be

switch (a <=> b)
{
case std::partial_order::unordered: // better?

I have met several cases when incomparability was possible but was
not checked or handled. That resulted with defects and even
security vulnerabilities. I hope that spaceship can make that more
transparent.

2) As rule the comparisons of composites are implemented by combining
comparisons of components. I hope that spaceship will make that process
less error-prone and resulting code more elegant (and may be even more
efficient).

There can be even more beneficial usages but I still perceive it as
complicated operator that is easy to misuse and hard to explain.

> It is a labour (and therefore bug) saving device, rather than for
> application code.

That is one reason of my dislike, it is most hard to define a line
between application code and what else there is.

David Brown

unread,
Sep 22, 2018, 8:50:44 AM9/22/18
to
if (comparable(a, b)) ... // Much better

The spaceship operator may be a fine way to implement "comparable", but
I like names with meanings.

> I have met several cases when incomparability was possible but was
> not checked or handled. That resulted with defects and even
> security vulnerabilities. I hope that spaceship can make that more
> transparent.

Perhaps, but again I am not sure it would result in the most readable
code. (But it would be better than code that didn't do the necessary
checks.)

>
> 2) As rule the comparisons of composites are implemented by combining
> comparisons of components. I hope that spaceship will make that process
> less error-prone and resulting code more elegant (and may be even more
> efficient).
>
> There can be even more beneficial usages but I still perceive it as
> complicated operator that is easy to misuse and hard to explain.
>

Yes, that's why I expect it will have more use buried along with other
code that is hard to explain, rather than in application code.

>> It is a labour (and therefore bug) saving device, rather than for
>> application code.
>
> That is one reason of my dislike, it is most hard to define a line
> between application code and what else there is.
>

I'd say it is /impossible/ to define such a line, not just hard. That
doesn't mean the distinction can't be useful. (Imagine finding a line
between "light blue" and "dark blue" - you won't fine one. That won't
stop you describing something as "light blue" or "dark blue".)

I'd expect to see spaceships in more general code such as class and
template libraries, rather than in the application-specfic code that
uses these classes and functions.


Öö Tiib

unread,
Sep 22, 2018, 10:10:45 AM9/22/18
to
That is how to make it more elegant/readable. I was more about the core
issue. The "can of worms" when comparing polymorphic objects, optionals,
variants or floating point variables. You must have met related issues
too. With floating points there is additionally that "almost equal"/
"close enough" issue. The spaceship can help to notice, to explain and
to handle those issues in code.

> > I have met several cases when incomparability was possible but was
> > not checked or handled. That resulted with defects and even
> > security vulnerabilities. I hope that spaceship can make that more
> > transparent.
>
> Perhaps, but again I am not sure it would result in the most readable
> code. (But it would be better than code that didn't do the necessary
> checks.)

Neither am I. What is fun about new things is to invent usages. Some of
those may become "idiomatic" later.

>
> >
> > 2) As rule the comparisons of composites are implemented by combining
> > comparisons of components. I hope that spaceship will make that process
> > less error-prone and resulting code more elegant (and may be even more
> > efficient).
> >
> > There can be even more beneficial usages but I still perceive it as
> > complicated operator that is easy to misuse and hard to explain.
> >
>
> Yes, that's why I expect it will have more use buried along with other
> code that is hard to explain, rather than in application code.
>
> >> It is a labour (and therefore bug) saving device, rather than for
> >> application code.
> >
> > That is one reason of my dislike, it is most hard to define a line
> > between application code and what else there is.
> >
>
> I'd say it is /impossible/ to define such a line, not just hard. That
> doesn't mean the distinction can't be useful. (Imagine finding a line
> between "light blue" and "dark blue" - you won't fine one. That won't
> stop you describing something as "light blue" or "dark blue".)
>
> I'd expect to see spaceships in more general code such as class and
> template libraries, rather than in the application-specfic code that
> uses these classes and functions.

I suspect that we will actually see it all over the code and coding
standards banning it and so on. It was so with three-way goto of
FORTRAN that received and keeps receiving major curses to this day.
People want to feel clever and I have no right to blame them since
I want sometimes to feel clever too. ;)

David Brown

unread,
Sep 22, 2018, 10:36:59 AM9/22/18
to
On 22/09/18 16:10, Öö Tiib wrote:
> On Saturday, 22 September 2018 15:50:44 UTC+3, David Brown wrote:
>> On 22/09/18 14:12, Öö Tiib wrote:

>>> I have met several cases when incomparability was possible but was
>>> not checked or handled. That resulted with defects and even
>>> security vulnerabilities. I hope that spaceship can make that more
>>> transparent.
>>
>> Perhaps, but again I am not sure it would result in the most readable
>> code. (But it would be better than code that didn't do the necessary
>> checks.)
>
> Neither am I. What is fun about new things is to invent usages. Some of
> those may become "idiomatic" later.

Fair enough ("fun" is a good enough reason for many things). I can only
hope that at least some of these idiomatic uses are legible :-)

>>
>> I'd expect to see spaceships in more general code such as class and
>> template libraries, rather than in the application-specfic code that
>> uses these classes and functions.
>
> I suspect that we will actually see it all over the code and coding
> standards banning it and so on. It was so with three-way goto of
> FORTRAN that received and keeps receiving major curses to this day.

That sounds believable.

> People want to feel clever and I have no right to blame them since
> I want sometimes to feel clever too. ;)
>

No, it is okay to blame people for bad habits even when you have them
yourself. I want other people to write /better/ code than I do, not
merely as good code.


Sam

unread,
Sep 22, 2018, 1:19:34 PM9/22/18
to
Keith Thompson writes:

> C++20 is adding a "three-way comparison operator", <=>, inspired by
> Perl's "spaceship operator". It compares two values and yields less,
> equal, or greater (of type std::strong_ordering), with some added
> complications for floating-point operands. Using it with a switch
> statement would be similar to what you (sarcastically, I presume)
> propose.

Last time I checked, <=> is not required to return -1, 0, or 1, but a
negative value, zero, or a positive value, just like Perl.

This would preclude it from being useful in a switch, by itself.

Christian Gollwitzer

unread,
Sep 22, 2018, 1:50:54 PM9/22/18
to
Am 21.09.18 um 19:25 schrieb james...@alumni.caltech.edu:
>> (looks like this topic was cross-posted so maybe some ambiguity about
>> what lang we're talking about here.)
>>
>> in C++ this works fine: if (a < ( b < c ? b : false))
>
> It works, in the sense that it has well-defined behavior so long as a,
> b, and c have appropriate types. I'm not sure there's any easy way to
> describe what it does. It's certainly not equivalent to either the
> current meaning of a < b < c, nor the proposed new meaning for that
> expression.

I think he wanted to mean it the proposed meaning with short circuiting
on the second comparison. For floating point, it would do this if the
"false" is replaced by either -Inf or NaN. *

OTOH this is so "tricky" that I would hide it in a macro or template,
and then we could simply use a more standard expression:

template <typename T>
bool in_range(T a, T x, T b) {
return (a < x) ? (x < b) : false
}

Christian

* I don't recall how to create thise special things from C++, why aren't
they accepted as floating point literals like this, instead of
std::mumble<ugly_template>::infinity() ?

Öö Tiib

unread,
Sep 22, 2018, 2:14:10 PM9/22/18
to
With object pointers or integral types <=> will be most likely
required to return a value of type std::strong_ordering, with
floating point types std::partial_ordering and with function
pointers and pointer to member std::strong_equality.
Values of each of those types are likely discrete so useful with
switch. However what we talk about is C++20 so no one can predict
the future.

Keith Thompson

unread,
Sep 23, 2018, 6:17:48 PM9/23/18
to
For integer operands, the result is of type std::strong_ordering, and
the value is one of std::less, std::equal, or std::greater.

I had assumed that std::strong_ordering is an enumerated type, which
would allow its use in switch statements, but in fact it's a class type.
(It's possible C++20 has some feature that permits its use in a switch
statement. I haven't checked.)

Details are in
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/n4727.pdf

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Siri Cruise

unread,
Sep 23, 2018, 11:37:56 PM9/23/18
to
In article <ln8t3ra...@kst-u.example.com>, Keith Thompson <ks...@mib.org>
wrote:

> > Last time I checked, <=> is not required to return -1, 0, or 1, but a
> > negative value, zero, or a positive value, just like Perl.
> >
> > This would preclude it from being useful in a switch, by itself.
>
> For integer operands, the result is of type std::strong_ordering, and
> the value is one of std::less, std::equal, or std::greater.

#define STRONGORDER(x) ((int)copysign(1, x))

This will return -1, 0, or 1 for an arithmetic type x.


#define STRONGORDER2(x, y) ((int)copysign(1, (double)(x)-(double)(y)))

The cast is to allow comparison of unsigned in a natural way.

Wouter Verhelst

unread,
Sep 24, 2018, 2:04:18 AM9/24/18
to
On 9/21/18 9:53 PM, Rick C. Hodgin wrote:
> On 9/21/2018 3:12 PM, Siri Cruise wrote:
>> Doesn't everyone want to program in 1966 Fortran?
>
> People often make fun of Fortran,

Yes, but that's not the FORTRAN (sic) from 1966, which has evolved
massively since, not only in the official recommended capitalization.

> but it's still a first-tier
> compiler receiving modern optimizations provided for by Intel
> (along with their C++ compiler).

And it's a first-tier compiler in the gcc set of compilers, too:
https://gcc.gnu.org/wiki/GFortran

>     https://software.intel.com/en-us/intel-compilers
>
> I regularly hear speakers on compute-related videos report
> that their scientists say there isn't a better language to
> express the formulas they use for calculation than in Fortran.

Yup, it definitely is not a fringe language. When I run this on my
laptop (this looks up how many source packages in Debian build-depend on
the gfortran compiler):

wouter@gangtai:~$ grep-dctrl -FBuild-Depends -sPackage gfortran <
/var/lib/apt/lists/ftp.be.debian.org_debian_dists_sid_main_source_Sources|sort
-u|wc -l

... then the result is 204. It includes things like BLAS (which itself
is a dependency for LAPACK) and FFTW, which are fairly often-used
scientific libraries (although I haven't got the faintest idea as to how
they would be used). Hilariously, it also includes llvm-toolchain-7
(you'd think they'd bootstrap their own fortran compiler rather than
using the GNU one). It doesn't include indirect build-dependencies
though, so there are more things that somehow require fortran code than
what's in that list; I believe pypi is one of them.

Wouter Verhelst

unread,
Sep 24, 2018, 2:20:15 AM9/24/18
to
On 9/22/18 11:08 AM, Wouter Verhelst wrote:
> It doesn't include indirect build-dependencies
> though, so there are more things that somehow require fortran code than
> what's in that list; I believe pypi is one of them.

That last statement is, of couse, complete nonsense. It should read
"numpy", because pypi is a repository of python packages, not a python
package :-)

Sorry for the confusion (but then I'm not really a python programmer)

guinne...@gmail.com

unread,
Sep 24, 2018, 4:01:47 AM9/24/18
to
On Sunday, 23 September 2018 23:17:49 UTC+1, Keith Thompson wrote:
> Sam <s...@email-scan.com> writes:
> > Keith Thompson writes:
> >> C++20 is adding a "three-way comparison operator", <=>, inspired by
> >> Perl's "spaceship operator". It compares two values and yields less,
> >> equal, or greater (of type std::strong_ordering), with some added
> >> complications for floating-point operands. Using it with a switch
> >> statement would be similar to what you (sarcastically, I presume)
> >> propose.
> >
> > Last time I checked, <=> is not required to return -1, 0, or 1, but a
> > negative value, zero, or a positive value, just like Perl.
> >
> > This would preclude it from being useful in a switch, by itself.
>
> For integer operands, the result is of type std::strong_ordering, and
> the value is one of std::less, std::equal, or std::greater.

std::less and std::greater are class templates in <functional>.
std::equal is a class template in <algorithm>.

As I read it, the three-way comparison operator will yield values of

std::strong_ordering::less,
std::strong_ordering::equal,
std::strong_ordering::greater

However, as you suspect below, these turn out to be individual instances of
std::strong_ordering itself, rendering them ineligible as switch() cases.

> I had assumed that std::strong_ordering is an enumerated type, which
> would allow its use in switch statements, but in fact it's a class type.
> (It's possible C++20 has some feature that permits its use in a switch
> statement. I haven't checked.)

I believe usage is intended to follow this pattern (which compiles (with
-std=c++2a) under the latest experimental version of clang++):

#include <compare>
#include <iostream>

void test( int const a, int const b )
{
auto const x = a <=> b;

if ( std::is_lt( x ) )
{
std::cout << "less" << std::endl;
}

if ( std::is_lteq( x ) )
{
std::cout << "less-or-equal" << std::endl;
}

if ( std::is_eq( x ) )
{
std::cout << "equal" << std::endl;
}

if ( std::is_gteq( x ) )
{
std::cout << "greater-or-equal" << std::endl;
}

if ( std::is_gt( x ) )
{
std::cout << "greater" << std::endl;
}

if ( std::is_neq( x ) )
{
std::cout << "not-equal" << std::endl;
}
}

int main()
{
test( 10, 20 );
test( 10, 10 );
test( 20, 10 );
Indeed, although the wording surrounding this topic is probably incomplete.
One has to first discover std::is_eq() et al; then discover that they take
arguments of type std::weak_ordering or std::partial_ordering; then note that
std::strong_ordering has conversion operators to std::weak_ordering and to
std::partial_ordering and that the three-way comparison yields a value of type
std::strong_ordering.

An example, in the standard, would be welcome.

Ben Bacarisse

unread,
Sep 24, 2018, 6:32:23 AM9/24/18
to
Siri Cruise <chine...@yahoo.com> writes:

> In article <ln8t3ra...@kst-u.example.com>, Keith Thompson <ks...@mib.org>
> wrote:
>
>> > Last time I checked, <=> is not required to return -1, 0, or 1, but a
>> > negative value, zero, or a positive value, just like Perl.
>> >
>> > This would preclude it from being useful in a switch, by itself.
>>
>> For integer operands, the result is of type std::strong_ordering, and
>> the value is one of std::less, std::equal, or std::greater.
>
> #define STRONGORDER(x) ((int)copysign(1, x))
>
> This will return -1, 0, or 1 for an arithmetic type x.

I can't see how that can ever be zero. Also, "arithmetic type" includes
the complex types. I think the phrase "real arithmetic type" is the one
you want.

> #define STRONGORDER2(x, y) ((int)copysign(1, (double)(x)-(double)(y)))
>
> The cast is to allow comparison of unsigned in a natural way.

Obviously this has the same problem of never being equal to zero, but it
will also run into trouble where unsigned a type has a range that goes
beyond that which 'double' can represent exactly.

--
Ben.

Vir Campestris

unread,
Sep 24, 2018, 4:54:08 PM9/24/18
to
On 20/09/2018 11:50, Bart wrote:
> I don't think I've ever considered that aspect, probably few have,
> although many will know that C started off with a ridiculously large
> number of precedences.

... which is why I bracket everything. I'm "agnostic" in this - I don't
know the full order of precedences, and I don't believe you do either.

Nobody has even satisfactorily explained to me why

2 + 3 * 4 isn't 20. The way every kid learning arithmetic assumes it
will be.

Except it's always been that way. Or mention the mathematician's a+bc,
which means "a + product of b and c". Which is just premature
optimisation :)

Andy

Bart

unread,
Sep 24, 2018, 6:16:10 PM9/24/18
to
On 24/09/2018 21:53, Vir Campestris wrote:
> On 20/09/2018 11:50, Bart wrote:
>> I don't think I've ever considered that aspect, probably few have,
>> although many will know that C started off with a ridiculously large
>> number of precedences.
>
> ... which is why I bracket everything. I'm "agnostic" in this - I don't
> know the full order of precedences, and I don't believe you do either.

Tim Rentsch does. I think he expects everyone else to as well (as you
might notice in his code).

--
bart

Jorgen Grahn

unread,
Sep 25, 2018, 4:20:51 PM9/25/18
to
On Mon, 2018-09-24, Vir Campestris wrote:
...
> Nobody has even satisfactorily explained to me why
>
> 2 + 3 * 4 isn't 20. The way every kid learning arithmetic assumes it
> will be.

I learned in school (around the age of eleven) that it's 14.
I'd be really surprised if these rules varied by country or fashion in
education; are you saying they do?

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

Alf P. Steinbach

unread,
Sep 25, 2018, 9:44:01 PM9/25/18
to
On 24.09.2018 22:53, Vir Campestris wrote:
> On 20/09/2018 11:50, Bart wrote:
>> I don't think I've ever considered that aspect, probably few have,
>> although many will know that C started off with a ridiculously large
>> number of precedences.
>
> ... which is why I bracket everything. I'm "agnostic" in this - I don't
> know the full order of precedences, and I don't believe you do either.
>
> Nobody has even satisfactorily explained to me why
>
> 2 + 3 * 4 isn't 20. The way every kid learning arithmetic assumes it
> will be.

A kilo gram is a thousand grams, k = 1000.

Five kilograms plus two kilograms, that's seven thousand grams.

So, the usual precedence rules are practical for common speech.

Ditto formal mathematical reasoning:

5kg + 2kg = (5k + 2k) × g = (5 × 1000 + 2 × 1000) × g = ?

So, they're also practical for formal math work, in the same way.


> Except it's always been that way. Or mention the mathematician's  a+bc,
> which means "a + product of b and c". Which is just premature
> optimisation :)

Just rules that support clear and concise communication, C³.


Cheers!,

- Alf

Paavo Helde

unread,
Sep 26, 2018, 12:46:40 AM9/26/18
to
In mathematics, I suspect this is just because multiplication is a more
complicated operation than addition, so one wants to do it with smaller
numbers and sum the results later. Parens can be dropped for brevity if
the formula follows this "default" representation.

In Fortran of course they had to retain these rules of course because it
helped with translating formulas (yep, that's the name!) from textbooks
and notebooks into computer programs. And C++ derives from Fortran, not
from Lisp. So there we are.




Bart

unread,
Sep 26, 2018, 5:38:49 AM9/26/18
to
On 26/09/2018 05:46, Paavo Helde wrote:

> In Fortran ...

>And C++ derives from Fortran, not from Lisp.

That's rather a long stretch. (For a start, Fortran was
case-insensitive, line-oriented, 1-based, had statement-based I/O, and
used keywords to introduce subroutines.)

I was going to go on to say that it's still closer to Fortran than Lisp,
but actually I'm not sure....


--
bart

Paavo Helde

unread,
Sep 26, 2018, 5:56:06 AM9/26/18
to
All quotes from Wikipedia:

"C with Classes" was the predecessor to C++.
"C with Classes" added features to the C compiler.
The name of C was chosen simply as the next after B.
B was derived from BCPL.
BCPL was a response to difficulties with its predecessor, CPL.
CPL was heavily influenced by ALGOL 60.
Algol was designed to avoid some of the perceived problems with FORTRAN.







Bart

unread,
Sep 26, 2018, 6:16:45 AM9/26/18
to
As I said, a long stretch.

Fortran (I'm thinking of Fortran IV) did have expressions, assignments,
variables, types, declarations, conditional statements, I/O, loops, goto
and subroutines, features which have been pretty much universally copied
by other languages, although a few are starting to drop some of those.

By that measure, *every* language is derived from Fortran! Just because
it was the first to use arbitrary expressions in source code.

(FWIW the languages I design still have all those 5 characteristics I
mentioned, but I hardly think of it as based on Fortran.)


--
bart

Alf P. Steinbach

unread,
Sep 26, 2018, 7:05:40 AM9/26/18
to
I'm not sure, but I think Plankalkül, cirka 1942?, had expressions.

Let me google that. Wait.

Oh, I may be wrong. Wikpedia provides the following example of a
Plankalkül program to compute the maximum of three variables:


P1 max3 (V0[:8.0],V1[:8.0],V2[:8.0]) → R0[:8.0]
max(V0[:8.0],V1[:8.0]) → Z1[:8.0]
max(Z1[:8.0],V2[:8.0]) → R0[:16.0}]
END
P2 max (V0[:8.0],V1[:8.0]) → R0[:8.0]
V0[:8.0] → Z1[:8.0]
(Z1[:8.0] < V1[:8.0]) → V1[:8.0] → Z1[:8.0]
Z1[:8.0] → R0[:8.0]
END


... and that doesn't look very expression-like.

Still, maybe the languages that Grace Hopper worked on before COBOL?

Googling again...

No, she entered that field late, despite my impression. It seems one of
the first languages with general expressions was called “George”, now
also known as the Laning and Zierler system, from 1952. Example program:

1 x = 0,
z = 1 - x2/2 + x4/2·3·4 - x6/2·3·4·5·6
+ x8/2·3·4·5·6·7·8 - x10/2·3·4·5·6·7·8·9·10,

PRINT x, z.
e = x - 1.05,
CP 1,
STOP


> (FWIW the languages I design still have all those 5 characteristics I
> mentioned, but I hardly think of it as based on Fortran.)

Algol was a revolutionary language in two or three ways:

* It introduced the Backus-Naur form (BNF) for expressing the syntax.
* It introduced the idea of a compiler whose parts could be connected
either via pipes/files or as co-routines, depending on how much main
memory was available, as I recall. But I may remember incorrectly.
* It introduced the notion of a specification bug, which inadvertently
resulted in “call by name” and the stack frame machinery to handle that.

Some people maintain that Algol should be remembered as the first
structured language (routines, loops and decision constructs).

Bah, I like my points much better. :)


Cheers!,

- Alf

James Kuyper

unread,
Sep 26, 2018, 8:15:56 AM9/26/18
to
On 09/24/2018 04:53 PM, Vir Campestris wrote:
> On 20/09/2018 11:50, Bart wrote:
>> I don't think I've ever considered that aspect, probably few have,
>> although many will know that C started off with a ridiculously large
>> number of precedences.
>
> ... which is why I bracket everything. I'm "agnostic" in this - I don't
> know the full order of precedences, and I don't believe you do either.

It's not that hard to learn, though it is certainly beyond Bart's
capabilities. Most of the precedence orders seem intuitively obvious to
me - perhaps because I understand why it's more convenient for operator
precedence to both exist and for the order to be defined the way it is.
The operator precedences that I have trouble remembering, are precisely
the ones that the original designers have since conceded were mistakes.

> Nobody has even satisfactorily explained to me why
>
> 2 + 3 * 4 isn't 20. The way every kid learning arithmetic assumes it
> will be.

I can't remember what I assumed about such things more than a
half-century ago, but if I did make that assumption, I'm sure that my
teachers promptly corrected that mistake. The concept of operator
precedence long predates C itself, or indeed any other computer language.

Tim Rentsch

unread,
Sep 26, 2018, 11:58:41 AM9/26/18
to
Vir Campestris <vir.cam...@invalid.invalid> writes:

> On 20/09/2018 11:50, Bart wrote:
>
>> I don't think I've ever considered that aspect, probably few have,
>> although many will know that C started off with a ridiculously large
>> number of precedences.
>
> ... which is why I bracket everything. I'm "agnostic" in this - I
> don't know the full order of precedences, and I don't believe you do
> either.

You may see a trolling followup from Bart making comments about me
and my beliefs. Please ignore these comments. Bart does not
speak for me. Whether intentionally or not, he has misrepresented
both earlier remarks I have made and my beliefs on the subject.

My current policy is not to respond to any posting of Bart's,
whatever the circumstances. Sometimes I may read them, and
other times I may not, but in any case no response will be
given.

Bart

unread,
Sep 26, 2018, 12:32:34 PM9/26/18
to
On 26/09/2018 16:58, Tim Rentsch wrote:
> Vir Campestris <vir.cam...@invalid.invalid> writes:
>
>> On 20/09/2018 11:50, Bart wrote:
>>
>>> I don't think I've ever considered that aspect, probably few have,
>>> although many will know that C started off with a ridiculously large
>>> number of precedences.
>>
>> ... which is why I bracket everything. I'm "agnostic" in this - I
>> don't know the full order of precedences, and I don't believe you do
>> either.
>
> You may see a trolling followup from Bart making comments about me
> and my beliefs. Please ignore these comments. Bart does not
> speak for me. Whether intentionally or not, he has misrepresented
> both earlier remarks I have made and my beliefs on the subject.

People can do their own searching for Tim's posts on the subject (mostly
in c.l.c) and make up their minds. But I expect they will show my
remarks weren't wildly off the mark.


> My current policy is not to respond to any posting of Bart's,
> whatever the circumstances.

Funny, I tend to do the same with yours, although mostly because the
topics you post on are so dull.

This post is for the group's benefit.

Paavo Helde

unread,
Sep 26, 2018, 1:40:20 PM9/26/18
to
On 26.09.2018 13:16, Bart wrote:
> Fortran (I'm thinking of Fortran IV) did have expressions, assignments,
> variables, types, declarations, conditional statements, I/O, loops, goto
> and subroutines, features which have been pretty much universally copied
> by other languages, although a few are starting to drop some of those.
>
> By that measure, *every* language is derived from Fortran! Just because
> it was the first to use arbitrary expressions in source code.

As a counter-example, Lisp is still very much alive, also in Emacs and
AutoCAD for example, and does not contain infix expressions or
precedence rules (at least according to my limited knowledge). Google
says APL does not have precedence rules either.

So I would say not every language is "derived from Fortran" regarding
expression syntax. But many are indeed.

bitrex

unread,
Sep 26, 2018, 9:29:28 PM9/26/18
to
On 09/22/2018 01:50 PM, Christian Gollwitzer wrote:
> Am 21.09.18 um 19:25 schrieb james...@alumni.caltech.edu:
>>> (looks like this topic was cross-posted so maybe some ambiguity about
>>> what lang we're talking about here.)
>>>
>>> in C++ this works fine: if (a < ( b < c ? b : false))
>>
>> It works, in the sense that it has well-defined behavior so long as a,
>> b, and c have appropriate types. I'm not sure there's any easy way to
>> describe what it does. It's certainly not equivalent to either the
>> current meaning of a < b < c, nor the proposed new meaning for that
>> expression.
>
> I think he wanted to mean it the proposed meaning with short circuiting
> on the second comparison. For floating point, it would do this if the
> "false" is replaced by either -Inf or NaN. *

Ya something like that :)

> OTOH this is so "tricky" that I would hide it in a macro or template,
> and then we could simply use a more standard expression:
>
> template <typename T>
> bool in_range(T a, T x, T b) {
>     return (a < x) ? (x < b) : false
> }
>
>     Christian
>
> * I don't recall how to create thise special things from C++, why aren't
> they accepted as floating point literals like this, instead of
> std::mumble<ugly_template>::infinity() ?
>

This should return false if the set is not a strict monotonic-increasing
ordering, and the greatest element if it is, with C++11-compliant
one-line constexprs. It seems correct but I haven't thoroughly tested
maybe y'all could double-check it for errors:

#include <iostream>

template <typename T>
constexpr auto compare(T a) -> decltype(a) {
return a;
}

template <typename T>
constexpr auto compare(T a, T b)
-> decltype(compare(a > 0 ? (a < b ? b : false) : (a < b ? a :
false))) {
return compare(a > 0 ? (a < b ? b : false) : (a < b ? a : false));
}

template <typename T>
constexpr auto compare(T a, T b, T c)
-> decltype(compare((compare(a, b)) ? compare(b, c) : false)) {
return compare((compare(a, b)) ? compare(b, c) : false);
}
template <typename T, typename... Rest>
constexpr auto compare(T a, T b, T c, Rest... rest) -> T {
return compare(compare(a, b), c, compare(rest...));
}

int main() {
/* Bad Fib */
std::cout << compare(1, 2, 3, 5, 8, -13, 21, 34, 55) << std::endl;

/* Good Fib */
std::cout << compare(1, 2, 3, 5, 8, 13, 21, 34, 55) << std::endl;
}

Vir Campestris

unread,
Sep 27, 2018, 4:41:38 PM9/27/18
to
On 26/09/2018 18:40, Paavo Helde wrote:
> Google says APL does not have precedence rules either.

APL is burned into my brain. I'm scarred by it - traumatised even.

It was strict right to left evaluation. No precedence.

Andy

Vir Campestris

unread,
Sep 27, 2018, 4:41:43 PM9/27/18
to
On 26/09/2018 05:46, Paavo Helde wrote:
>
> In mathematics, I suspect this is just because multiplication is a more
> complicated operation than addition, so one wants to do it with smaller
> numbers and sum the results later. Parens can be dropped for brevity if
> the formula follows this "default" representation.
>
> In Fortran of course they had to retain these rules of course because it
> helped with translating formulas (yep, that's the name!) from textbooks
> and notebooks into computer programs. And C++ derives from Fortran, not
> from Lisp. So there we are.

Thank you. Finally an explanation that makes sense.

Andy

Öö Tiib

unread,
Sep 28, 2018, 1:37:30 PM9/28/18
to
On Friday, 21 September 2018 17:19:36 UTC+3, bitrex wrote:
> On 09/20/2018 02:21 AM, David Brown wrote:
> > On 20/09/18 01:47, bitrex wrote:
> >> On 09/19/2018 05:23 PM, David Brown wrote:
> >>> On 19/09/18 19:43, bitrex wrote:
> >>>> On 09/19/2018 12:23 PM, Scott wrote:
> >>>>> On Wed, 19 Sep 2018 11:01:45 -0400, "Rick C. Hodgin"
> >>>>> <rick.c...@gmail.com> wrote:
> >>>>>
> >>>>>> Can anyone think of a reason why this type of operation shouldn't
> >>>>>> be a valid syntax in a (C/C++)-like lanuage, added to that new
> >>>>>> language with these features:
> >>>>>>
> >>>>>>      if (a <= b < c)
> >>>>>>          printf("b is [a, c)\n");
> >>>>>>
> >>>>>>      if (a < b <= c)
> >>>>>>          printf("b is (a, c]\n");
> >>>>>>
> >>>>>>      if (a < b < c)
> >>>>>>          printf("b is (a, c)\n");
> >>>>>>
> >>>>>> And even these:
> >>>>>>
> >>>>>>      if (a < b < c < d)
> >>>>>>          prinf("b is (a, c)\nc is (b, d)\n");
> >>>>>>
> >>>>>>      if (a < b < c < d < e)
> >>>>>>          prinf("b is (a, c)\nc is (b, d)\nd is (c, e)\n");
> >>>>> ...
> >>>>>
> >>>>> What nonsense are you trolling about now? Everybody knows that these
> >>>>> are already perfectly valid constructs in C with simple and
> >>>>> unambiguous semantics.
> >>>>>
> >>>>
> >>>> it's ambiguous enough in that gcc emits a warning:
> >>>
> >>> gcc does not give a warning because it is ambiguous - the code is not
> >>> ambiguous in C.  gcc gives a warning because it is probably incorrect
> >>> code.
> >>>
> >>> There is nothing wrong with the idea of making expressions like these
> >>> match their mathematical meaning.  But it would be confusing to do so
> >>> in a C-like language.
> >>
> >> Yes, I meant "ambiguous" in the sense of my brain might not have been
> >> able to immediately intuit what behavior that form of expression
> >> invoked. Given that it compiled and it does not appear to invoke any
> >> undefined behavior I know of then surely the compiler is not confused.
> >> It will do something, whatever it is.
> >
> > Ah, you mean /your/ interpretation of it is ambiguous - not that the C
> > itself is ambiguous.  Yes, I can appreciate that.  There is nothing for
> > it but to learn the way C interprets this type of expression - and then,
> > like all sane C programmers, avoid writing it.
>
> I've been writing C/C++ off and on since ALL THE WAY BACK in the late
> 1990s and I can't say it ever occurred to me to write a test like if ( a
> < b < c ) I'd probably just write (a < b && b < c) by force of habit.
> the former is very high-school math-y vs. Boolean logic.

Note that the '(a < b && b < c)' is likely sub-optimal code when
those are integer or pointer values and it is meant as a check if
that 'b' is inside of some fixed range 'a' to 'c' (that is quite
frequent purpose of that expression). Issue is that implementations
tend to generate two branches there. Sometimes these are generated
even when the a and c are known compile time. Branches are quite
expensive on modern out-of-order hardware.

Scott Lurndal

unread,
Sep 28, 2018, 2:23:01 PM9/28/18
to
=?UTF-8?B?w5bDtiBUaWli?= <oot...@hot.ee> writes:
>On Friday, 21 September 2018 17:19:36 UTC+3, bitrex wrote:

>>=20
>> I've been writing C/C++ off and on since ALL THE WAY BACK in the late=20
>> 1990s and I can't say it ever occurred to me to write a test like if ( a=
>=20
>> < b < c ) I'd probably just write (a < b && b < c) by force of habit.=20
>> the former is very high-school math-y vs. Boolean logic.
>
>Note that the '(a < b && b < c)' is likely sub-optimal code when
>those are integer or pointer values and it is meant as a check if
>that 'b' is inside of some fixed range 'a' to 'c' (that is quite
>frequent purpose of that expression). Issue is that implementations
>tend to generate two branches there. Sometimes these are generated
>even when the a and c are known compile time.

Modern compilers understand that and generate optimal code if
requested (e.g. -O3):

$ cat /tmp/a.c
#include <string.h>
#include <stdio.h>

int
main(int argc, const char **argv)
{
unsigned long a = strtoul(argv[1], NULL, 0);
unsigned long b = strtoul(argv[2], NULL, 0);
unsigned long c = strtoul(argv[3], NULL, 0);

if (a < b && b < c) return 1;
return 0;
}

gcc 4.6 without -O:

40054b: 48 8b 45 e8 mov -0x18(%rbp),%rax
40054f: 48 3b 45 f0 cmp -0x10(%rbp),%rax
400553: 73 11 jae 400566 <main+0xa2>
400555: 48 8b 45 f0 mov -0x10(%rbp),%rax
400559: 48 3b 45 f8 cmp -0x8(%rbp),%rax
40055d: 73 07 jae 400566 <main+0xa2>
40055f: b8 01 00 00 00 mov $0x1,%eax
400564: eb 05 jmp 40056b <main+0xa7>
400566: b8 00 00 00 00 mov $0x0,%eax
40056b: c9 leaveq
40056c: c3 retq

gcc 4.6 with -O3 (no branches):

40051e: 48 8b 1c 24 mov (%rsp),%rbx
400522: 48 39 c5 cmp %rax,%rbp
400525: 0f 92 c0 setb %al
400528: 49 39 ec cmp %rbp,%r12
40052b: 48 8b 6c 24 08 mov 0x8(%rsp),%rbp
400530: 0f 92 c2 setb %dl
400533: 4c 8b 64 24 10 mov 0x10(%rsp),%r12
400538: 48 83 c4 18 add $0x18,%rsp
40053c: 21 d0 and %edx,%eax
40053e: 0f b6 c0 movzbl %al,%eax
400541: c3 retq

> Branches are quite expensive on modern out-of-order hardware.

And modern out-of-order hardware has quite effective branch
prediction for most workloads.

james...@alumni.caltech.edu

unread,
Sep 28, 2018, 2:30:11 PM9/28/18
to
On Friday, September 28, 2018 at 1:37:30 PM UTC-4, Öö Tiib wrote:
> On Friday, 21 September 2018 17:19:36 UTC+3, bitrex wrote:
...
> > I've been writing C/C++ off and on since ALL THE WAY BACK in the late
> > 1990s and I can't say it ever occurred to me to write a test like if ( a
> > < b < c ) I'd probably just write (a < b && b < c) by force of habit.
> > the former is very high-school math-y vs. Boolean logic.
>
> Note that the '(a < b && b < c)' is likely sub-optimal code when
> those are integer or pointer values and it is meant as a check if
> that 'b' is inside of some fixed range 'a' to 'c' (that is quite
> frequent purpose of that expression). Issue is that implementations
> tend to generate two branches there. Sometimes these are generated
> even when the a and c are known compile time. Branches are quite
> expensive on modern out-of-order hardware.

Could you demonstrate this, at high optimization levels? This involves
several lower level questions:
1. What does the generated code look like when this problem comes up?
2. What is the better code you'd want to be generated?
3. What is the alternative way of writing equivalent C code for this
purpose that will cause the better code to be generated?
4. Can you identify a particular compiler, with particular options
selected, including high optimization levels, which produces the poorer
code for if(a < b && b < c), but produces the better code for your
preferred alternative?

Naively, if your alternative C code is in fact equivalent, I'd expect
any decent C compiler, at least when high optimization levels are
selected, to generate the same better code for either form of the C
code.

Öö Tiib

unread,
Sep 28, 2018, 4:23:58 PM9/28/18
to
On Friday, 28 September 2018 21:30:11 UTC+3, james...@alumni.caltech.edu wrote:
> On Friday, September 28, 2018 at 1:37:30 PM UTC-4, Öö Tiib wrote:
> > On Friday, 21 September 2018 17:19:36 UTC+3, bitrex wrote:
> ...
> > > I've been writing C/C++ off and on since ALL THE WAY BACK in the late
> > > 1990s and I can't say it ever occurred to me to write a test like if ( a
> > > < b < c ) I'd probably just write (a < b && b < c) by force of habit.
> > > the former is very high-school math-y vs. Boolean logic.
> >
> > Note that the '(a < b && b < c)' is likely sub-optimal code when
> > those are integer or pointer values and it is meant as a check if
> > that 'b' is inside of some fixed range 'a' to 'c' (that is quite
> > frequent purpose of that expression). Issue is that implementations
> > tend to generate two branches there. Sometimes these are generated
> > even when the a and c are known compile time. Branches are quite
> > expensive on modern out-of-order hardware.
>
> Could you demonstrate this, at high optimization levels? This involves
> several lower level questions:
> 1. What does the generated code look like when this problem comes up?
> 2. What is the better code you'd want to be generated?
> 3. What is the alternative way of writing equivalent C code for this
> purpose that will cause the better code to be generated?

Most often we use [start end) ranges in C and C++. It is really
implementation detail if we store representation of such range as start
and end or as start and length since the length is end - start.

For such ranges a point is in range when (1) start <= point and point < end.
Also the point is in range when (2) (unsigned)(point - start) < length.

The formula (2) did generate better code that did run quicker on
iOS device than formula (1). I surely checked that it was also
better in particular case (lot of verifying if points were in
ranges or not) on Mac.

> 4. Can you identify a particular compiler, with particular options
> selected, including high optimization levels, which produces the poorer
> code for if(a < b && b < c), but produces the better code for your
> preferred alternative?

I noticed that few years ago. With Clang of XCode for iOS, processor
was perhaps ARMv7-A. Not sure if it was Objective-C or C++ but in
given context these are all likely just front end to same compiler.
Efficiency was important since it is good idea not to drain the
battery of handheld device with processing.

So I did bit more detailed profiling and bench-marking than usually
needed. I can dig around in archives and for particular benchmarks
and see what it shows with current compilers.

> Naively, if your alternative C code is in fact equivalent, I'd expect
> any decent C compiler, at least when high optimization levels are
> selected, to generate the same better code for either form of the C
> code.

That is what I often say myself. But sometimes there can be that even
most mundane expressions can be tinkered into somewhat more fitting
with the context of usage, platform and compiler.

David Brown

unread,
Sep 29, 2018, 7:44:18 AM9/29/18
to
The two formula are close to equivalent, but not entirely. They
certainly rely on the condition that a <= c, and may be different in
cases near wrappings (I haven't thought about it enough to tell).

I would want compilers to make such transformations when it makes a
difference and it is known to be correct - I don't want to have to start
messing with these myself. I have certainly seen gcc make such
transformations.

I also expect compilers to make the right choice between code with
conditional jumps and alternatives such as conditional moves. In some
cases, branches are cheaper (even on modern OOO cpus) - in other cases,
they are more expensive. What is almost certainly true is that the
compiler writers know more about it than the average C++ programmer.


So write your source code in the clearest and most logical manner. Then
if you are convinced the compiler is generating sub-optimal code, file a
"missed optimisation" bug for it.

Öö Tiib

unread,
Sep 29, 2018, 10:15:08 AM9/29/18
to
Exactly! The formulas can give different results when the length
overflows (undefined behavior) or when it is negative. On case
of ranges such situations are programming errors and should be
dealt with where the ranges are formed, but with just any a, b, c
these are perfectly legal situations.

> I would want compilers to make such transformations when it makes a
> difference and it is known to be correct - I don't want to have to start
> messing with these myself. I have certainly seen gcc make such
> transformations.
>
> I also expect compilers to make the right choice between code with
> conditional jumps and alternatives such as conditional moves. In some
> cases, branches are cheaper (even on modern OOO cpus) - in other cases,
> they are more expensive. What is almost certainly true is that the
> compiler writers know more about it than the average C++ programmer.

C++ compiler does not understand concept of ranges. At least not
before "Concept" of "Range" is added to it. ;) It is up to its
implementers to make std::vector as 3 pointers or pointer and 2
lengths but it can't decide if we store representation of our
"ranges" as start and end pair or as start and length pair.

Latter was optimal on described case, but it may be sub-optimal
on some other.

Now if we choose start and length then it still does not understand
what these are and so it can not convert 'a < b && b < a + len'
into '(unsigned)(b - a) < len' because negative 'len' may make
sense in some other context and then the formulas are different.

>
> So write your source code in the clearest and most logical manner. Then
> if you are convinced the compiler is generating sub-optimal code, file a
> "missed optimisation" bug for it.

I generally write code in most simple and logical manner because it is
only about 5% of it that can possibly alter perceivable performance.
The attempt here was to post something thought-provoking
as alternative to stuff from ramine, leigh and rick that does
not matter any to me.

Richard Damon

unread,
Sep 29, 2018, 1:09:28 PM9/29/18
to
I presume APL didn't try to define an operator precedence because it has
SO many operators no one could agree on an order, let alone attempt to
remember it.
It is loading more messages.
0 new messages