Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c++ > #84173 > unrolled thread

Constexpr evaluation of heavy stuff

Started byAndrey Tarasevich <andreytarasevich@hotmail.com>
First post2022-05-18 16:17 -0700
Last post2022-05-19 18:33 +0200
Articles 20 on this page of 39 — 14 participants

Back to article view | Back to comp.lang.c++


Contents

  Constexpr evaluation of heavy stuff Andrey Tarasevich <andreytarasevich@hotmail.com> - 2022-05-18 16:17 -0700
    Re: Constexpr evaluation of heavy stuff "Alf P. Steinbach" <alf.p.steinbach@gmail.com> - 2022-05-19 09:19 +0200
      Re: Constexpr evaluation of heavy stuff David Brown <david.brown@hesbynett.no> - 2022-05-19 11:59 +0200
        Re: Constexpr evaluation of heavy stuff "Alf P. Steinbach" <alf.p.steinbach@gmail.com> - 2022-05-19 12:37 +0200
          Re: Constexpr evaluation of heavy stuff David Brown <david.brown@hesbynett.no> - 2022-05-19 14:49 +0200
            Re: Constexpr evaluation of heavy stuff Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2022-05-19 06:19 -0700
              Re: Constexpr evaluation of heavy stuff Paavo Helde <eesnimi@osa.pri.ee> - 2022-05-19 20:32 +0300
                Re: Constexpr evaluation of heavy stuff Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2022-05-20 01:12 -0700
                  Re: Constexpr evaluation of heavy stuff Juha Nieminen <nospam@thanks.invalid> - 2022-05-20 08:37 +0000
                    Re: Constexpr evaluation of heavy stuff Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2022-05-20 02:21 -0700
                      Re: Constexpr evaluation of heavy stuff Ben <ben.usenet@bsb.me.uk> - 2022-05-20 11:25 +0100
                        Re: Constexpr evaluation of heavy stuff David Brown <david.brown@hesbynett.no> - 2022-05-20 12:47 +0200
                          Re: Constexpr evaluation of heavy stuff Ben <ben.usenet@bsb.me.uk> - 2022-05-20 12:13 +0100
                            Re: Constexpr evaluation of heavy stuff David Brown <david.brown@hesbynett.no> - 2022-05-20 14:27 +0200
                            Re: Constexpr evaluation of heavy stuff Öö Tiib <ootiib@hot.ee> - 2022-05-20 06:33 -0700
                              Re: Constexpr evaluation of heavy stuff Paavo Helde <eesnimi@osa.pri.ee> - 2022-05-20 17:15 +0300
                                Re: Constexpr evaluation of heavy stuff Christian Gollwitzer <auriocus@gmx.de> - 2022-05-20 18:57 +0200
                                  Re: Constexpr evaluation of heavy stuff Paavo Helde <eesnimi@osa.pri.ee> - 2022-05-20 22:31 +0300
                                    Re: Constexpr evaluation of heavy stuff Manfred <noname@add.invalid> - 2022-05-21 21:56 +0200
                                      Re: Constexpr evaluation of heavy stuff Öö Tiib <ootiib@hot.ee> - 2022-05-21 23:24 -0700
                                        Re: Constexpr evaluation of heavy stuff Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2022-05-22 04:56 -0700
                                          Re: Constexpr evaluation of heavy stuff Öö Tiib <ootiib@hot.ee> - 2022-05-22 06:15 -0700
                                        Re: Constexpr evaluation of heavy stuff Manfred <noname@add.invalid> - 2022-05-22 21:46 +0200
                                          Re: Constexpr evaluation of heavy stuff "Alf P. Steinbach" <alf.p.steinbach@gmail.com> - 2022-05-22 23:23 +0200
                                            Re: Constexpr evaluation of heavy stuff Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2022-05-22 16:26 -0700
                                            Re: Constexpr evaluation of heavy stuff Manfred <noname@add.invalid> - 2022-05-23 17:24 +0200
                                            Re: Constexpr evaluation of heavy stuff Richard Damon <Richard@Damon-Family.org> - 2022-05-23 19:41 -0400
                                              Re: Constexpr evaluation of heavy stuff scott@slp53.sl.home (Scott Lurndal) - 2022-05-24 16:15 +0000
                                            Re: Constexpr evaluation of heavy stuff Tim Rentsch <tr.17687@z991.linuxsc.com> - 2022-05-24 05:37 -0700
                        Re: Constexpr evaluation of heavy stuff Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2022-05-20 04:19 -0700
                          Re: Constexpr evaluation of heavy stuff Ben <ben.usenet@bsb.me.uk> - 2022-05-20 12:32 +0100
                      Re: Constexpr evaluation of heavy stuff Juha Nieminen <nospam@thanks.invalid> - 2022-05-20 10:40 +0000
                    Re: Constexpr evaluation of heavy stuff David Brown <david.brown@hesbynett.no> - 2022-05-20 12:31 +0200
                      Re: Constexpr evaluation of heavy stuff Juha Nieminen <nospam@thanks.invalid> - 2022-05-20 10:43 +0000
                        Re: Constexpr evaluation of heavy stuff David Brown <david.brown@hesbynett.no> - 2022-05-20 14:52 +0200
            Re: Constexpr evaluation of heavy stuff "Alf P. Steinbach" <alf.p.steinbach@gmail.com> - 2022-05-19 15:34 +0200
      Re: Constexpr evaluation of heavy stuff "Alf P. Steinbach" <alf.p.steinbach@gmail.com> - 2022-05-19 17:03 +0200
    Re: Constexpr evaluation of heavy stuff Tim Rentsch <tr.17687@z991.linuxsc.com> - 2022-05-19 08:53 -0700
      Re: Constexpr evaluation of heavy stuff Marcel Mueller <news.5.maazl@spamgourmet.org> - 2022-05-19 18:33 +0200

Page 1 of 2  [1] 2  Next page →


#84173 — Constexpr evaluation of heavy stuff

FromAndrey Tarasevich <andreytarasevich@hotmail.com>
Date2022-05-18 16:17 -0700
SubjectConstexpr evaluation of heavy stuff
Message-ID<t63uqq$hnm$1@dont-email.me>
Hello

Just for the sake of experiment I tried this

   #include <array>

   template <std::size_t N> constexpr auto collect_primes()
   {
     std::array<unsigned, N> primes = { 2, 3 };
     std::size_t n_primes = 2;

     for (unsigned i = 0; n_primes < N; ++i)
     {
       unsigned value = 6u * (i / 2u + 1u) + i % 2u * 2u - 1;

       std::size_t i_prime = 0;
       for (; i_prime < n_primes; ++i_prime)
         if (value % primes[i_prime] == 0)
           break;

       if (i_prime == n_primes)
         primes[n_primes++] = value;
     }

     return primes;
   }

   int main()
   {
     constexpr auto primes = collect_primes<78498>();
     ...
   }

Given the above code GCC will [attempt to] build the table of primes at 
compile time. Command line options like

   -fconstexpr-ops-limit=10000000000

are needed to prevent it from aborting this overly heavy compile-time 
evaluation prematurely. And this compile-time evaluation takes hours and 
hours and hours and... well... honestly, I haven't been able to see it 
to completion.

If I get rid of the `constexpr`, i.e. switch to regular run-time 
evaluation, the above table will be built in about 15 seconds on the 
same machine.

Reducing the table size to 10000 results in 8 minute `constexpr` 
evaluation vs. 0.25 second run-time table generation.

Granted, it is naive to expect the same efficiency from compile-time 
`constexpr` evaluation as we expect from `-O3` run-time code. They can't 
just paste the `constexpr` code into a temp file, compile it with `-O3` 
and run it to obtain the result (can they?). The compiler will 
apparently have to "pseudo-run" the code inside an "abstract virtual C++ 
machine" of sorts... But the actual difference in performance is still 
rather startling. What could be the primary reason behind `constexpr` 
evaluation being so much slower? Is it just a quality of implementation 
issue? Or is there a bunch of fundamental reasons it will always be 
slow? I see that GCC consumes huge amounts of memory wile compiling this 
code... Is it trying to fully unwrap the cycle/logic, which could be the 
reason for massive memory consumption and subsequent slowdown once 
swapping begins?

And yes, I understand that using `constexpr` evaluation to find all 
primes in the first million is definitely not a proper use of the 
feature. This is intended to be a contrived example.

-- 
Best regards,
Andrey

[toc] | [next] | [standalone]


#84174

From"Alf P. Steinbach" <alf.p.steinbach@gmail.com>
Date2022-05-19 09:19 +0200
Message-ID<t64r23$ncc$1@dont-email.me>
In reply to#84173
On 19 May 2022 01:17, Andrey Tarasevich wrote:
>    #include <array>
> 
>    template <std::size_t N> constexpr auto collect_primes()
>    {
>      std::array<unsigned, N> primes = { 2, 3 };
>      std::size_t n_primes = 2;
> 
>      for (unsigned i = 0; n_primes < N; ++i)
>      {
>        unsigned value = 6u * (i / 2u + 1u) + i % 2u * 2u - 1;
> 
>        std::size_t i_prime = 0;
>        for (; i_prime < n_primes; ++i_prime)
>          if (value % primes[i_prime] == 0)
>            break;
> 
>        if (i_prime == n_primes)
>          primes[n_primes++] = value;
>      }
> 
>      return primes;
>    }
> [snip]

Not what you're asking (which I think is important! but alas have no 
answer), but consider disregarding silly context independent herd 
opinion about what language and library features should and should not 
be used, and just use the features appropriate for the task at hand:


#include <array>
template< int n, class Item > using Array_ = std::array<Item, n>;

template< int capacity, class Item >
struct Simple_static_vector_{ int count; Array_<capacity, Item> items; };

template< int n_primes >
constexpr auto collect_primes() -> Array_<n_primes, int>
{
     Simple_static_vector_<n_primes, int> primes = {2, {2, 3}};
     for( int candidate = 5; primes.count < n_primes; candidate += 2 ) {
         for( int i = 0; i < primes.count; ++i ) {
             if( candidate % primes.items[i] == 0 ) {
                 goto continue_candidate;
             }
         }
         primes.items[primes.count++] = candidate;
         continue_candidate: ;
     }
     return primes.items;
}


Code features different from original:

* Self-describing, including the alias with more reasonable parameter 
order (like "5 int", not "int 5") for std library thing.
* Signed types like `int` for numbers, not unsigned types like `size_t`.
* `goto` where appropriate (would be nice with a labeled `continue`!).

Well, OK, the C++ core guidelines also recommends signed integer types 
for numbers, so that's not so very non-herd-ish in general. It's just an 
affront to some herds that have adopted religious articles about size 
types etc. But since the religious herds are very vocal I included that.


- Alf

[toc] | [prev] | [next] | [standalone]


#84175

FromDavid Brown <david.brown@hesbynett.no>
Date2022-05-19 11:59 +0200
Message-ID<t654dl$cek$1@dont-email.me>
In reply to#84174
On 19/05/2022 09:19, Alf P. Steinbach wrote:
> On 19 May 2022 01:17, Andrey Tarasevich wrote:
>>    #include <array>
>>
>>    template <std::size_t N> constexpr auto collect_primes()
>>    {
>>      std::array<unsigned, N> primes = { 2, 3 };
>>      std::size_t n_primes = 2;
>>
>>      for (unsigned i = 0; n_primes < N; ++i)
>>      {
>>        unsigned value = 6u * (i / 2u + 1u) + i % 2u * 2u - 1;
>>
>>        std::size_t i_prime = 0;
>>        for (; i_prime < n_primes; ++i_prime)
>>          if (value % primes[i_prime] == 0)
>>            break;
>>
>>        if (i_prime == n_primes)
>>          primes[n_primes++] = value;
>>      }
>>
>>      return primes;
>>    }
>> [snip]
> 
> Not what you're asking (which I think is important! but alas have no 
> answer), but consider disregarding silly context independent herd 
> opinion about what language and library features should and should not 
> be used, and just use the features appropriate for the task at hand:
> 
> 
> #include <array>
> template< int n, class Item > using Array_ = std::array<Item, n>;
> 
> template< int capacity, class Item >
> struct Simple_static_vector_{ int count; Array_<capacity, Item> items; };
> 
> template< int n_primes >
> constexpr auto collect_primes() -> Array_<n_primes, int>
> {
>      Simple_static_vector_<n_primes, int> primes = {2, {2, 3}};
>      for( int candidate = 5; primes.count < n_primes; candidate += 2 ) {
>          for( int i = 0; i < primes.count; ++i ) {
>              if( candidate % primes.items[i] == 0 ) {
>                  goto continue_candidate;
>              }
>          }
>          primes.items[primes.count++] = candidate;
>          continue_candidate: ;
>      }
>      return primes.items;
> }
> 
> 
> Code features different from original:
> 
> * Self-describing, including the alias with more reasonable parameter 
> order (like "5 int", not "int 5") for std library thing.

That is, at best, a very subjective viewpoint.  Your code is different, 
and I am sure that to /your/ eyes it is more "self-describing" - but I 
doubt if it is significantly clearer for anyone else.  (Stepping through 
the prime candidates by 2, rather than Andrey's more complicated jumps, 
is clearer.  But that's a minor point.)

> * Signed types like `int` for numbers, not unsigned types like `size_t`.
> * `goto` where appropriate (would be nice with a labeled `continue`!).
> 

This is not a case where "goto" helps the code structure.  (It would be 
interesting to know if it affected the compile time at all.)

I agree on the convenience of "int" over "std::size_t" for a loop 
variable, but I'd say the choice is personal preference or habit rather 
than some kind of "religious herd mentality".  C++ is a very flexible 
language and the same code can be written in many ways - I think you 
should place the bar a lot higher before criticising other peoples code 
as "silly" or "herd opinion".

On the other hand, signed int is probably less efficient than unsigned 
int here, since there are divisions involved - but that may vary by 
target and compiler, and I have no idea if it is significant for 
compile-time calculations.

> Well, OK, the C++ core guidelines also recommends signed integer types 
> for numbers, so that's not so very non-herd-ish in general. It's just an 
> affront to some herds that have adopted religious articles about size 
> types etc. But since the religious herds are very vocal I included that.
> 
> 
> - Alf
> 

[toc] | [prev] | [next] | [standalone]


#84176

From"Alf P. Steinbach" <alf.p.steinbach@gmail.com>
Date2022-05-19 12:37 +0200
Message-ID<t656ke$otk$1@dont-email.me>
In reply to#84175
On 19 May 2022 11:59, David Brown wrote:
> On 19/05/2022 09:19, Alf P. Steinbach wrote:
>> On 19 May 2022 01:17, Andrey Tarasevich wrote:
>>>    #include <array>
>>>
>>>    template <std::size_t N> constexpr auto collect_primes()
>>>    {
>>>      std::array<unsigned, N> primes = { 2, 3 };
>>>      std::size_t n_primes = 2;
>>>
>>>      for (unsigned i = 0; n_primes < N; ++i)
>>>      {
>>>        unsigned value = 6u * (i / 2u + 1u) + i % 2u * 2u - 1;
>>>
>>>        std::size_t i_prime = 0;
>>>        for (; i_prime < n_primes; ++i_prime)
>>>          if (value % primes[i_prime] == 0)
>>>            break;
>>>
>>>        if (i_prime == n_primes)
>>>          primes[n_primes++] = value;
>>>      }
>>>
>>>      return primes;
>>>    }
>>> [snip]
>>
>> Not what you're asking (which I think is important! but alas have no 
>> answer), but consider disregarding silly context independent herd 
>> opinion about what language and library features should and should not 
>> be used, and just use the features appropriate for the task at hand:
>>
>>
>> #include <array>
>> template< int n, class Item > using Array_ = std::array<Item, n>;
>>
>> template< int capacity, class Item >
>> struct Simple_static_vector_{ int count; Array_<capacity, Item> items; };
>>
>> template< int n_primes >
>> constexpr auto collect_primes() -> Array_<n_primes, int>
>> {
>>      Simple_static_vector_<n_primes, int> primes = {2, {2, 3}};
>>      for( int candidate = 5; primes.count < n_primes; candidate += 2 ) {
>>          for( int i = 0; i < primes.count; ++i ) {
>>              if( candidate % primes.items[i] == 0 ) {
>>                  goto continue_candidate;
>>              }
>>          }
>>          primes.items[primes.count++] = candidate;
>>          continue_candidate: ;
>>      }
>>      return primes.items;
>> }
>>
>>
>> Code features different from original:
>>
>> * Self-describing, including the alias with more reasonable parameter 
>> order (like "5 int", not "int 5") for std library thing.
> 
> That is, at best, a very subjective viewpoint.  Your code is different, 
> and I am sure that to /your/ eyes it is more "self-describing" - but I 
> doubt if it is significantly clearer for anyone else.  (Stepping through 
> the prime candidates by 2, rather than Andrey's more complicated jumps, 
> is clearer.  But that's a minor point.)
> 
>> * Signed types like `int` for numbers, not unsigned types like `size_t`.
>> * `goto` where appropriate (would be nice with a labeled `continue`!).
>>
> 
> This is not a case where "goto" helps the code structure.  (It would be 
> interesting to know if it affected the compile time at all.)

The `goto` here

* removes a needless check with `if` (desirable), and

* reduces the scope of the loop variable (desirable), because

* it expresses a state directly by execution position (desirable) 
instead of in the relation between the value of variable and another 
named value,

... i.e. the third point also removes at least one indirection.

That's pretty objective improvement in each point, so a 3 x improvement.


> I agree on the convenience of "int" over "std::size_t" for a loop 
> variable, but I'd say the choice is personal preference or habit rather 
> than some kind of "religious herd mentality".

As mentioned the core C++ guidelines recommend signed types for numbers.

I don't agree with everything in the guidelines because some advice 
there is clearly the result of compromises, then often of the "doesn't 
really matter except to those who needs a rule" kind, but this is not: 
it's not a compromise, and it does matter.

A counter-productive choice such as choosing unsigned type for numbers 
can be personal preference, of course. E.g. because of ignorance of the 
consequences or of the advantages of alternatives. But to some such a 
choice is religious: they choose not only to chastise people who use 
signed integers for numbers, but expend an extraordinary amount of 
needless work on using the to them right-sized unsigned types, which 
work includes hunting down and fixing bugs resulting from that choice.

Since that's nuts I call it religious, for short.

Because it's the same psychological mechanism at work as for religious 
issues in general, namely, herd membership (the strongest human 
instinct) through adoption of the herd's delusions, often very 
conservative delusions, sometimes viewpoints that once had some merit.


>  C++ is a very flexible 
> language and the same code can be written in many ways - I think you 
> should place the bar a lot higher before criticising other peoples code 
> as "silly" or "herd opinion".

I criticized the herd opinions with those words.

I criticized the other person's code by showing an alternative way to 
express it.

And that is not what you wrote (quoted).


> On the other hand, signed int is probably less efficient than unsigned 
> int here, since there are divisions involved - but that may vary by 
> target and compiler, and I have no idea if it is significant for 
> compile-time calculations.

This claim about slowish signed division is highly dubious for general 
computers, but may hold for DSP and some other embedded computers.

It's my impression that it's pretty much in the myth category.

So I believe the burden of proof is on you. :)

It would be interesting if it holds, because I haven't encountered that 
argument.

If so then it should probably be mentioned in the guidelines, as a cost.


>> Well, OK, the C++ core guidelines also recommends signed integer types 
>> for numbers, so that's not so very non-herd-ish in general. It's just 
>> an affront to some herds that have adopted religious articles about 
>> size types etc. But since the religious herds are very vocal I 
>> included that.


Cheers,

- Alf

[toc] | [prev] | [next] | [standalone]


#84177

FromDavid Brown <david.brown@hesbynett.no>
Date2022-05-19 14:49 +0200
Message-ID<t65ed3$jgh$1@dont-email.me>
In reply to#84176
On 19/05/2022 12:37, Alf P. Steinbach wrote:
> On 19 May 2022 11:59, David Brown wrote:
>> On 19/05/2022 09:19, Alf P. Steinbach wrote:
>>> On 19 May 2022 01:17, Andrey Tarasevich wrote:
>>>>    #include <array>
>>>>
>>>>    template <std::size_t N> constexpr auto collect_primes()
>>>>    {
>>>>      std::array<unsigned, N> primes = { 2, 3 };
>>>>      std::size_t n_primes = 2;
>>>>
>>>>      for (unsigned i = 0; n_primes < N; ++i)
>>>>      {
>>>>        unsigned value = 6u * (i / 2u + 1u) + i % 2u * 2u - 1;
>>>>
>>>>        std::size_t i_prime = 0;
>>>>        for (; i_prime < n_primes; ++i_prime)
>>>>          if (value % primes[i_prime] == 0)
>>>>            break;
>>>>
>>>>        if (i_prime == n_primes)
>>>>          primes[n_primes++] = value;
>>>>      }
>>>>
>>>>      return primes;
>>>>    }
>>>> [snip]
>>>
>>> Not what you're asking (which I think is important! but alas have no 
>>> answer), but consider disregarding silly context independent herd 
>>> opinion about what language and library features should and should 
>>> not be used, and just use the features appropriate for the task at hand:
>>>
>>>
>>> #include <array>
>>> template< int n, class Item > using Array_ = std::array<Item, n>;
>>>
>>> template< int capacity, class Item >
>>> struct Simple_static_vector_{ int count; Array_<capacity, Item> 
>>> items; };
>>>
>>> template< int n_primes >
>>> constexpr auto collect_primes() -> Array_<n_primes, int>
>>> {
>>>      Simple_static_vector_<n_primes, int> primes = {2, {2, 3}};
>>>      for( int candidate = 5; primes.count < n_primes; candidate += 2 ) {
>>>          for( int i = 0; i < primes.count; ++i ) {
>>>              if( candidate % primes.items[i] == 0 ) {
>>>                  goto continue_candidate;
>>>              }
>>>          }
>>>          primes.items[primes.count++] = candidate;
>>>          continue_candidate: ;
>>>      }
>>>      return primes.items;
>>> }
>>>
>>>
>>> Code features different from original:
>>>
>>> * Self-describing, including the alias with more reasonable parameter 
>>> order (like "5 int", not "int 5") for std library thing.
>>
>> That is, at best, a very subjective viewpoint.  Your code is 
>> different, and I am sure that to /your/ eyes it is more 
>> "self-describing" - but I doubt if it is significantly clearer for 
>> anyone else.  (Stepping through the prime candidates by 2, rather than 
>> Andrey's more complicated jumps, is clearer.  But that's a minor point.)
>>
>>> * Signed types like `int` for numbers, not unsigned types like `size_t`.
>>> * `goto` where appropriate (would be nice with a labeled `continue`!).
>>>
>>
>> This is not a case where "goto" helps the code structure.  (It would 
>> be interesting to know if it affected the compile time at all.)
> 
> The `goto` here
> 
> * removes a needless check with `if` (desirable), and
> 

True, but the check is pretty harmless.  (Note that in generated code, 
where efficiency is important, these kinds of checks will be optimised 
into a jump by the compiler.)

> * reduces the scope of the loop variable (desirable), because

Yes.

> 
> * it expresses a state directly by execution position (desirable) 
> instead of in the relation between the value of variable and another 
> named value,

I realise there are good points to the "goto" here.  I just don't think 
they are worth the bad points of the unstructured statement.

> 
> ... i.e. the third point also removes at least one indirection.
> 
> That's pretty objective improvement in each point, so a 3 x improvement.

That's not how this works.  The points you make, the scoring you give 
them, the points you choose to omit - they are all subjective 
preferences.  (As are my preferences - I don't claim anything different.)

> 
>> I agree on the convenience of "int" over "std::size_t" for a loop 
>> variable, but I'd say the choice is personal preference or habit 
>> rather than some kind of "religious herd mentality".
> 
> As mentioned the core C++ guidelines recommend signed types for numbers.
> 
> I don't agree with everything in the guidelines because some advice 
> there is clearly the result of compromises, then often of the "doesn't 
> really matter except to those who needs a rule" kind, but this is not: 
> it's not a compromise, and it does matter.

In this particular situation, all the values are non-negative, and well 
below the range of int.  So int, unsigned int, size_t, or many other 
types would be fine both for the indexing and for the calculation 
values.  So no, it does not matter for correctness of code.  "int" is 
often a convenient default choice, but there are no direct advantages to 
it here - and a possible direct disadvantage for the calculations in 
that division is sometimes slower with signed types.

> 
> A counter-productive choice such as choosing unsigned type for numbers 
> can be personal preference, of course. E.g. because of ignorance of the 
> consequences or of the advantages of alternatives. But to some such a 
> choice is religious: they choose not only to chastise people who use 
> signed integers for numbers, but expend an extraordinary amount of 
> needless work on using the to them right-sized unsigned types, which 
> work includes hunting down and fixing bugs resulting from that choice.
> 
> Since that's nuts I call it religious, for short.

There is only one person in this thread who is making blanket claims 
about their style choices, and insisting that their personal subjective 
preferences are somehow the only true, objective and correct choice.  I 
realise it is not your intention, but the only person making 
"religious-style" claims is /you/.  (I am not keen on your use of the 
term "religious" here - I don't think your analogy is accurate enough 
and I don't think the tone helps your position at all.  An arrogant and 
condescending tone is not a substitute for justification.  But I'd 
really rather not get into an off-topic discussion on the psychology of 
religion here.)

> 
> Because it's the same psychological mechanism at work as for religious 
> issues in general, namely, herd membership (the strongest human 
> instinct) through adoption of the herd's delusions, often very 
> conservative delusions, sometimes viewpoints that once had some merit.
> 
> 
>>   C++ is a very flexible language and the same code can be written in 
>> many ways - I think you should place the bar a lot higher before 
>> criticising other peoples code as "silly" or "herd opinion".
> 
> I criticized the herd opinions with those words.
> 
> I criticized the other person's code by showing an alternative way to 
> express it.
> 
> And that is not what you wrote (quoted).
> 

You told the OP to "consider disregarding silly context independent herd 
opinion".  This is not a court of law - we do not need to argue over 
tiny details of wording.

> 
>> On the other hand, signed int is probably less efficient than unsigned 
>> int here, since there are divisions involved - but that may vary by 
>> target and compiler, and I have no idea if it is significant for 
>> compile-time calculations.
> 
> This claim about slowish signed division is highly dubious for general 
> computers, but may hold for DSP and some other embedded computers.
> 

Computers in general /are/ embedded, if you consider the numbers in use. 
  I know of situations where signed integer division can be measurably 
slower (or provide fewer optimisation opportunities) than unsigned 
division, but I have not seen the converse.  As I said, I have no idea 
about the timings for compile-time calculations - but I doubt if it is 
relevant there.

If you prefer to stick to x86 cpus, following the herd opinion that 
these are the only processors that matter, you can look at the timings 
of signed and unsigned integer division instructions for large numbers 
of processors in Agner Fog's timing tables:

<https://www.agner.org/optimize/instruction_tables.pdf>

A quick look at a few cases suggests it is common for signed integer 
division to be a little slower than unsigned integer division, even on 
quite modern devices.

Of course this is unlikely to make a measurable difference in practice - 
if code is performance-critical, you'd try to avoid integer division 
anyway (floating point division is usually much faster on modern "big" 
processors).  The point is, changing the unsigned type to a signed type 
is an unnecessary pessimisation, apparently motived by your own 
following of "herd mentality" :-)

> It's my impression that it's pretty much in the myth category.
> 
> So I believe the burden of proof is on you. :)
> 
> It would be interesting if it holds, because I haven't encountered that 
> argument.
> 
> If so then it should probably be mentioned in the guidelines, as a cost.
> 

The guidelines are not focused on code efficiency, but on more important 
issues of code correctness, readability, and maintainability (while 
avoiding serious inefficiencies).  I think it would be very rare to have 
the choice of signed or unsigned types determined mainly by the 
efficiency of integer division - but a prime sieve might conceivably be 
one of these rare cases where it is relevant.  However, I would be 
surprised to see a measurable difference in real code.


> 
>>> Well, OK, the C++ core guidelines also recommends signed integer 
>>> types for numbers, so that's not so very non-herd-ish in general. 
>>> It's just an affront to some herds that have adopted religious 
>>> articles about size types etc. But since the religious herds are very 
>>> vocal I included that.
> 
> 
> Cheers,
> 
> - Alf
> 

[toc] | [prev] | [next] | [standalone]


#84178

FromMalcolm McLean <malcolm.arthur.mclean@gmail.com>
Date2022-05-19 06:19 -0700
Message-ID<b4c80897-17a3-4951-a4a5-f3ca95c2f5d5n@googlegroups.com>
In reply to#84177
On Thursday, 19 May 2022 at 13:49:56 UTC+1, David Brown wrote:
>
> The guidelines are not focused on code efficiency, but on more important 
> issues of code correctness, readability, and maintainability (while 
> avoiding serious inefficiencies). I think it would be very rare to have 
> the choice of signed or unsigned types determined mainly by the 
> efficiency of integer division - but a prime sieve might conceivably be 
> one of these rare cases where it is relevant. However, I would be 
> surprised to see a measurable difference in real code.
> 
Most integers are inherently non-negative, usually because they count things
or index things. So it's natural when using a language that allows both
signed and unsigned types to think that "unsigned" is designed to cover
this type of usage. Nothing "religious" about it.

However intermediate calculations can often go negative, particularly with
indexing operations, if you need the item before the index you are working on  
(take value at i-1), or the item after (do while i < N-1). It's usually easier to allow 
the values to go negative that to try to handle the boundary conditions in 
modulus power of two arithmetic.

Then you often want an "error" value. If you can't read the "employee" data
file, you don't want to report zero employees, because that runs through the 
system and no-one gets paid. You want to report a negative number, forcing
caller to report an error.

So signed rather than unsigned types for integers that can't go negative is
usually better. 

[toc] | [prev] | [next] | [standalone]


#84193

FromPaavo Helde <eesnimi@osa.pri.ee>
Date2022-05-19 20:32 +0300
Message-ID<t65v09$bd9$1@dont-email.me>
In reply to#84178
19.05.2022 16:19 Malcolm McLean kirjutas:
> Most integers are inherently non-negative, usually because they count things
> or index things. So it's natural when using a language that allows both
> signed and unsigned types to think that "unsigned" is designed to cover
> this type of usage. Nothing "religious" about it.

Yes, that would be fine if the language provided such an unsigned type. 
Alas, in C and C++ there are no such unsigned arithmetic types. All we 
have got are some strange cyclic wrapover types, and the wrapover 
threshold is not even fixed (unsigned int can be 16 bits, 32 bits or 
more). There are very few numeric algorithms requiring such wrap-over types.

Why is this important? For signed ints wrap-over is UB, so if wrap-over 
happens one knows there is something wrong; one can also instrument the 
compiler to detect and report such bugs. Nothing like that can be done 
for unsigned types.

[toc] | [prev] | [next] | [standalone]


#84207

FromMalcolm McLean <malcolm.arthur.mclean@gmail.com>
Date2022-05-20 01:12 -0700
Message-ID<727d656f-7e08-4be2-a0c9-c2dc9d29b00cn@googlegroups.com>
In reply to#84193
On Thursday, 19 May 2022 at 18:33:13 UTC+1, Paavo Helde wrote:
> 19.05.2022 16:19 Malcolm McLean kirjutas: 
> > Most integers are inherently non-negative, usually because they count things 
> > or index things. So it's natural when using a language that allows both 
> > signed and unsigned types to think that "unsigned" is designed to cover 
> > this type of usage. Nothing "religious" about it.
> Yes, that would be fine if the language provided such an unsigned type. 
> Alas, in C and C++ there are no such unsigned arithmetic types. All we 
> have got are some strange cyclic wrapover types, and the wrapover 
> threshold is not even fixed (unsigned int can be 16 bits, 32 bits or 
> more). There are very few numeric algorithms requiring such wrap-over types. 
> 
> Why is this important? For signed ints wrap-over is UB, so if wrap-over 
> happens one knows there is something wrong; one can also instrument the 
> compiler to detect and report such bugs. Nothing like that can be done 
> for unsigned types.
>
That's certainly another advantage. (I tried to point this out  a few weeks
ago and received in reply that I shouldn't be programming if I couldn't
avoid arithmetical overflow!).
The snag is that to use it, you have to be able to put the compiler into a
mode where it traps on overflow instead of silently wrapping. Most
compilers don't have such a mode.

[toc] | [prev] | [next] | [standalone]


#84208

FromJuha Nieminen <nospam@thanks.invalid>
Date2022-05-20 08:37 +0000
Message-ID<t67jvc$1q94$1@gioia.aioe.org>
In reply to#84207
Malcolm McLean <malcolm.arthur.mclean@gmail.com> wrote:
> The snag is that to use it, you have to be able to put the compiler into a
> mode where it traps on overflow instead of silently wrapping. Most
> compilers don't have such a mode.

I'm not entirely sure what the point of that would be. What exactly should
happen in such a program if an unsigned arithmetic operation overflows or
underflows? Should the program end? Should the code throw an exception?
(What would you do if such an exception happens?) How should the program
behave in such a situation?

Also, should bit-shifting to the left trap if a 1-bit gets shifted out?
Ostensibly multiplying by a power of 2 should trap if it overflows.
Should bit-shifting to the left also do so (given that it's effectively
the exact same thing)?

Maybe it should behave like a few other programming languages which switch
from hardware-register-sized integers to software multiprecision integers
on any overflow? (But even then, what about underflow?)

If you want integers that grow as needed, just use GMP?

[toc] | [prev] | [next] | [standalone]


#84209

FromMalcolm McLean <malcolm.arthur.mclean@gmail.com>
Date2022-05-20 02:21 -0700
Message-ID<978b4a0b-0740-4b01-96b0-4b9e52e0de1an@googlegroups.com>
In reply to#84208
On Friday, 20 May 2022 at 09:37:19 UTC+1, Juha Nieminen wrote:
> Malcolm McLean <malcolm.ar...@gmail.com> wrote: 
> > The snag is that to use it, you have to be able to put the compiler into a 
> > mode where it traps on overflow instead of silently wrapping. Most 
> > compilers don't have such a mode.
> I'm not entirely sure what the point of that would be. What exactly should 
> happen in such a program if an unsigned arithmetic operation overflows or 
> underflows? Should the program end? Should the code throw an exception? 
> (What would you do if such an exception happens?) How should the program 
> behave in such a situation? 
> 
> Also, should bit-shifting to the left trap if a 1-bit gets shifted out? 
> Ostensibly multiplying by a power of 2 should trap if it overflows. 
> Should bit-shifting to the left also do so (given that it's effectively 
> the exact same thing)? 
> 
> Maybe it should behave like a few other programming languages which switch 
> from hardware-register-sized integers to software multiprecision integers 
> on any overflow? (But even then, what about underflow?) 
> 
> If you want integers that grow as needed, just use GMP?
>
Say we've got this code.

int width = getinteger():
int height = getinteger();
int Npixels = width* height;

There's a potential vulnerability there if Npixels wraps. An attacker might be able to construct
an input file that causes the program to access memory out of bounds in such a way as
to cause arbitrary code to be executed.

If the program terminates with an error message, that vulnerability is removed.

Usually, terminating will be better. The program won't process the image correctly, so ignoring
the problem won't help.

[toc] | [prev] | [next] | [standalone]


#84210

FromBen <ben.usenet@bsb.me.uk>
Date2022-05-20 11:25 +0100
Message-ID<87o7zs5wlt.fsf@bsb.me.uk>
In reply to#84209
Malcolm McLean <malcolm.arthur.mclean@gmail.com> writes:

> On Friday, 20 May 2022 at 09:37:19 UTC+1, Juha Nieminen wrote:
>> Malcolm McLean <malcolm.ar...@gmail.com> wrote: 
>> > The snag is that to use it, you have to be able to put the compiler into a 
>> > mode where it traps on overflow instead of silently wrapping. Most 
>> > compilers don't have such a mode.
>> I'm not entirely sure what the point of that would be. What exactly should 
>> happen in such a program if an unsigned arithmetic operation overflows or 
>> underflows? Should the program end? Should the code throw an exception? 
>> (What would you do if such an exception happens?) How should the program 
>> behave in such a situation? 
>> 
>> Also, should bit-shifting to the left trap if a 1-bit gets shifted out? 
>> Ostensibly multiplying by a power of 2 should trap if it overflows. 
>> Should bit-shifting to the left also do so (given that it's effectively 
>> the exact same thing)? 
>> 
>> Maybe it should behave like a few other programming languages which switch 
>> from hardware-register-sized integers to software multiprecision integers 
>> on any overflow? (But even then, what about underflow?) 
>> 
>> If you want integers that grow as needed, just use GMP?
>>
> Say we've got this code.
>
> int width = getinteger():
> int height = getinteger();
> int Npixels = width* height;
>
> There's a potential vulnerability there if Npixels wraps. An attacker
> might be able to construct an input file that causes the program to
> access memory out of bounds in such a way as to cause arbitrary code
> to be executed.

I found the suggestion (not from you) that UB is an advantage a very odd
one.  I would consider it too unpredictable to be any practical help.

But here, why not just make all the sizes unsigned?  You have to do some
bounds checking since the data come from a file which can be maliciously
constructed, so provided you do that check, a wrapped Npixels will
presumably just produce a junk result.

And, if you decide to add an overflow check, it's no harder using
unsigned.

-- 
Ben.

[toc] | [prev] | [next] | [standalone]


#84214

FromDavid Brown <david.brown@hesbynett.no>
Date2022-05-20 12:47 +0200
Message-ID<t67rjj$kk3$1@dont-email.me>
In reply to#84210
On 20/05/2022 12:25, Ben wrote:
> Malcolm McLean <malcolm.arthur.mclean@gmail.com> writes:
> 
>> On Friday, 20 May 2022 at 09:37:19 UTC+1, Juha Nieminen wrote:
>>> Malcolm McLean <malcolm.ar...@gmail.com> wrote:
>>>> The snag is that to use it, you have to be able to put the compiler into a
>>>> mode where it traps on overflow instead of silently wrapping. Most
>>>> compilers don't have such a mode.
>>> I'm not entirely sure what the point of that would be. What exactly should
>>> happen in such a program if an unsigned arithmetic operation overflows or
>>> underflows? Should the program end? Should the code throw an exception?
>>> (What would you do if such an exception happens?) How should the program
>>> behave in such a situation?
>>>
>>> Also, should bit-shifting to the left trap if a 1-bit gets shifted out?
>>> Ostensibly multiplying by a power of 2 should trap if it overflows.
>>> Should bit-shifting to the left also do so (given that it's effectively
>>> the exact same thing)?
>>>
>>> Maybe it should behave like a few other programming languages which switch
>>> from hardware-register-sized integers to software multiprecision integers
>>> on any overflow? (But even then, what about underflow?)
>>>
>>> If you want integers that grow as needed, just use GMP?
>>>
>> Say we've got this code.
>>
>> int width = getinteger():
>> int height = getinteger();
>> int Npixels = width* height;
>>
>> There's a potential vulnerability there if Npixels wraps. An attacker
>> might be able to construct an input file that causes the program to
>> access memory out of bounds in such a way as to cause arbitrary code
>> to be executed.
> 
> I found the suggestion (not from you) that UB is an advantage a very odd
> one.  I would consider it too unpredictable to be any practical help.

I see two advantages of UB.  One is that if your code never leads to 
overflows (and that should usually be the case, once you have everything 
tested and debugged), it gives the compiler some extra optimisation 
opportunities.  But the big one, IMHO, is that it means tools can treat 
the overflow as a mistake in the code.  That means you can use a tool 
that gives debugging aids when the error occurs, without changing the 
defined behaviour of correct code.

If I suspect my code has an error leading to signed integer overflow, I 
can use a toolchain with a "sanitizer" to catch the overflow and then 
fix the problem.  I can do that with no extra code, and no cost to the 
efficiency of the deployed program, precisely because it is undefined 
behaviour.  If the overflow was in unsigned arithmetic, I have no such 
help because the overflow is defined "correct" behaviour, as far as the 
language is concerned.

The point here is that when something has undefined behaviour, it is 
possible for the developer and toolchain to give it any kind of 
behaviour that they want (assuming the toolchain has the feature you 
need).  So you can pick the behaviour that helps most.

(Of course unsigned overflow does not stand out here - lots of bugs are 
in code that has fully defined behaviour.)

> 
> But here, why not just make all the sizes unsigned?  You have to do some
> bounds checking since the data come from a file which can be maliciously
> constructed, so provided you do that check, a wrapped Npixels will
> presumably just produce a junk result.
> 

The correct solution here is to remember the cardinal rule of secure and 
reliable programming - do not trust data coming from outside.  This is 
not a matter of arithmetic overflow, it is a matter of being sure the 
incoming data is valid.  Then there can be no overflow (assuming the 
programmer picked the right types and operations for the valid data ranges).

> And, if you decide to add an overflow check, it's no harder using
> unsigned.
> 

[toc] | [prev] | [next] | [standalone]


#84215

FromBen <ben.usenet@bsb.me.uk>
Date2022-05-20 12:13 +0100
Message-ID<87czg85udt.fsf@bsb.me.uk>
In reply to#84214
David Brown <david.brown@hesbynett.no> writes:

> On 20/05/2022 12:25, Ben wrote:
>> Malcolm McLean <malcolm.arthur.mclean@gmail.com> writes:
>> 
>>> On Friday, 20 May 2022 at 09:37:19 UTC+1, Juha Nieminen wrote:
>>>> Malcolm McLean <malcolm.ar...@gmail.com> wrote:
>>>>> The snag is that to use it, you have to be able to put the compiler into a
>>>>> mode where it traps on overflow instead of silently wrapping. Most
>>>>> compilers don't have such a mode.
>>>>
>>>> I'm not entirely sure what the point of that would be. What exactly should
>>>> happen in such a program if an unsigned arithmetic operation overflows or
>>>> underflows? Should the program end? Should the code throw an exception?
>>>> (What would you do if such an exception happens?) How should the program
>>>> behave in such a situation?
>>>>
>>>> Also, should bit-shifting to the left trap if a 1-bit gets shifted out?
>>>> Ostensibly multiplying by a power of 2 should trap if it overflows.
>>>> Should bit-shifting to the left also do so (given that it's effectively
>>>> the exact same thing)?
>>>>
>>>> Maybe it should behave like a few other programming languages which switch
>>>> from hardware-register-sized integers to software multiprecision integers
>>>> on any overflow? (But even then, what about underflow?)
>>>>
>>>> If you want integers that grow as needed, just use GMP?
>>>>
>>> Say we've got this code.
>>>
>>> int width = getinteger():
>>> int height = getinteger();
>>> int Npixels = width* height;
>>>
>>> There's a potential vulnerability there if Npixels wraps. An attacker
>>> might be able to construct an input file that causes the program to
>>> access memory out of bounds in such a way as to cause arbitrary code
>>> to be executed.
>>
>> I found the suggestion (not from you) that UB is an advantage a very odd
>> one.  I would consider it too unpredictable to be any practical help.
>
> I see two advantages of UB.
>
> One is that if your code never leads to overflows (and that should
> usually be the case, once you have everything tested and debugged), it
> gives the compiler some extra optimisation opportunities.  But the big
> one, IMHO, is that it means tools can treat the overflow as a mistake
> in the code.  That means you can use a tool that gives debugging aids
> when the error occurs, without changing the defined behaviour of
> correct code.

The suggestion was to prefer a program to be undefined rather than
fiddle about with well-defined but fancy modulo arithmetic (I am
paraphrasing -- the context has been lost).

It was not about UB in general.

> If I suspect my code has an error leading to signed integer overflow,
> I can use a toolchain with a "sanitizer" to catch the overflow and
> then fix the problem.

Yes, but would you avoid unsigned int and prefer signed in order to get
this "advantage"?  Seems an odd choice to me.

-- 
Ben.

[toc] | [prev] | [next] | [standalone]


#84218

FromDavid Brown <david.brown@hesbynett.no>
Date2022-05-20 14:27 +0200
Message-ID<t681fi$148$1@dont-email.me>
In reply to#84215
On 20/05/2022 13:13, Ben wrote:
> David Brown <david.brown@hesbynett.no> writes:
> 
>> On 20/05/2022 12:25, Ben wrote:
>>> Malcolm McLean <malcolm.arthur.mclean@gmail.com> writes:
>>>
>>>> On Friday, 20 May 2022 at 09:37:19 UTC+1, Juha Nieminen wrote:
>>>>> Malcolm McLean <malcolm.ar...@gmail.com> wrote:
>>>>>> The snag is that to use it, you have to be able to put the compiler into a
>>>>>> mode where it traps on overflow instead of silently wrapping. Most
>>>>>> compilers don't have such a mode.
>>>>>
>>>>> I'm not entirely sure what the point of that would be. What exactly should
>>>>> happen in such a program if an unsigned arithmetic operation overflows or
>>>>> underflows? Should the program end? Should the code throw an exception?
>>>>> (What would you do if such an exception happens?) How should the program
>>>>> behave in such a situation?
>>>>>
>>>>> Also, should bit-shifting to the left trap if a 1-bit gets shifted out?
>>>>> Ostensibly multiplying by a power of 2 should trap if it overflows.
>>>>> Should bit-shifting to the left also do so (given that it's effectively
>>>>> the exact same thing)?
>>>>>
>>>>> Maybe it should behave like a few other programming languages which switch
>>>>> from hardware-register-sized integers to software multiprecision integers
>>>>> on any overflow? (But even then, what about underflow?)
>>>>>
>>>>> If you want integers that grow as needed, just use GMP?
>>>>>
>>>> Say we've got this code.
>>>>
>>>> int width = getinteger():
>>>> int height = getinteger();
>>>> int Npixels = width* height;
>>>>
>>>> There's a potential vulnerability there if Npixels wraps. An attacker
>>>> might be able to construct an input file that causes the program to
>>>> access memory out of bounds in such a way as to cause arbitrary code
>>>> to be executed.
>>>
>>> I found the suggestion (not from you) that UB is an advantage a very odd
>>> one.  I would consider it too unpredictable to be any practical help.
>>
>> I see two advantages of UB.
>>
>> One is that if your code never leads to overflows (and that should
>> usually be the case, once you have everything tested and debugged), it
>> gives the compiler some extra optimisation opportunities.  But the big
>> one, IMHO, is that it means tools can treat the overflow as a mistake
>> in the code.  That means you can use a tool that gives debugging aids
>> when the error occurs, without changing the defined behaviour of
>> correct code.
> 
> The suggestion was to prefer a program to be undefined rather than
> fiddle about with well-defined but fancy modulo arithmetic (I am
> paraphrasing -- the context has been lost).
> 
> It was not about UB in general.

Sorry, I misunderstood.  Yes, I agree - you don't want undefined 
behaviour in the final code.  But you also don't want behaviour that is 
defined, but wrong for the code - as is often the case for unsigned 
integer overflow.  Using signed int, along with suitable debugging 
tools, might make it easier to find and fix problems than if you have 
used unsigned ints.

> 
>> If I suspect my code has an error leading to signed integer overflow,
>> I can use a toolchain with a "sanitizer" to catch the overflow and
>> then fix the problem.
> 
> Yes, but would you avoid unsigned int and prefer signed in order to get
> this "advantage"?  Seems an odd choice to me.
> 

Debugging is always a bit of an art-form (it has famously been said to 
be a harder than writing the code in the first place).  Certainly I 
would consider making such a change to a buggy program in order to find 
a problem, but it is unlikely that I'd pick signed over signed in the 
first place just to allow such debugging.  But then, it's rare that 
overflows occur in my code at all (at least compared to other bugs!).

I have, however, used sanitizers trapping signed integer overflow to 
find bugs in other peoples' code on a couple of occasions.

[toc] | [prev] | [next] | [standalone]


#84220

FromÖö Tiib <ootiib@hot.ee>
Date2022-05-20 06:33 -0700
Message-ID<8dc30c48-e2ee-43e8-93fb-79f54704b879n@googlegroups.com>
In reply to#84215
On Friday, 20 May 2022 at 14:14:06 UTC+3, Ben wrote:
> David Brown <david...@hesbynett.no> writes: 
> 
> > If I suspect my code has an error leading to signed integer overflow, 
> > I can use a toolchain with a "sanitizer" to catch the overflow and 
> > then fix the problem.
> 
> Yes, but would you avoid unsigned int and prefer signed in order to get 
> this "advantage"? Seems an odd choice to me. 

When 64 bit signed int overflows then there is either programming
defect in calculations or design defect that the type is misused for
something arcane.  Mundane stuff fits into it and for arcane purposes
we should use arbitrary precision number libs. 

Now if we build our program that uses signed integers for  everything
with likes of -ftrapv or -fsanitize=undefined then it traps precisely at
defect. Such defects can be quite cheap to fix. Not that odd to desire it.

Modular arithmetic around likes of 42949672956 however is typically
utterly useless. So there is well-defined behaviour going on that we do
not need that may lead to defects or wrong answers far from place
where that behaviour happened. Why is it better?

[toc] | [prev] | [next] | [standalone]


#84221

FromPaavo Helde <eesnimi@osa.pri.ee>
Date2022-05-20 17:15 +0300
Message-ID<t687pk$ilc$1@dont-email.me>
In reply to#84220
20.05.2022 16:33 Öö Tiib kirjutas:

> Modular arithmetic around likes of 42949672956 however is typically
> utterly useless. 
I agree in general, but I have found one use case. Suppose we have a 
string s which might or might not contain a separator like ':' and we 
want to either return the part after separator, or the whole string if 
there is no separator. This is the code:

return s.substr(s.find(':')+1);

[toc] | [prev] | [next] | [standalone]


#84224

FromChristian Gollwitzer <auriocus@gmx.de>
Date2022-05-20 18:57 +0200
Message-ID<t68hah$uuh$1@dont-email.me>
In reply to#84221
Am 20.05.22 um 16:15 schrieb Paavo Helde:
> 20.05.2022 16:33 Öö Tiib kirjutas:
> 
>> Modular arithmetic around likes of 42949672956 however is typically
>> utterly useless. 
> I agree in general, but I have found one use case. Suppose we have a 
> string s which might or might not contain a separator like ':' and we 
> want to either return the part after separator, or the whole string if 
> there is no separator. This is the code:
> 
> return s.substr(s.find(':')+1);
> 
And where exactly do you need mod "ridiculous number" here? WHy is that 
better than having a signed index variable, where negative values 
indicate invalid indices?

	Christian

[toc] | [prev] | [next] | [standalone]


#84225

FromPaavo Helde <eesnimi@osa.pri.ee>
Date2022-05-20 22:31 +0300
Message-ID<t68qa0$8b9$1@dont-email.me>
In reply to#84224
20.05.2022 19:57 Christian Gollwitzer kirjutas:
> Am 20.05.22 um 16:15 schrieb Paavo Helde:
>> 20.05.2022 16:33 Öö Tiib kirjutas:
>>
>>> Modular arithmetic around likes of 42949672956 however is typically
>>> utterly useless. 
>> I agree in general, but I have found one use case. Suppose we have a 
>> string s which might or might not contain a separator like ':' and we 
>> want to either return the part after separator, or the whole string if 
>> there is no separator. This is the code:
>>
>> return s.substr(s.find(':')+1);
>>
> And where exactly do you need mod "ridiculous number" here? WHy is that 
> better than having a signed index variable, where negative values 
> indicate invalid indices?

You are right, if std::string::find() returned a signed integer -1 for 
no-find, this code would work just as fine. The only thing is that it 
doesn't work that way, it returns size_t(-1) which is typically 
18446744073709551615ULL nowadays.

Coming to think of that, the only usage of unsigned types in my code 
happens because the C++ standard library uses them for size_t and 
related types.

[toc] | [prev] | [next] | [standalone]


#84231

FromManfred <noname@add.invalid>
Date2022-05-21 21:56 +0200
Message-ID<t6bg4k$f9c$1@gioia.aioe.org>
In reply to#84225
On 5/20/2022 9:31 PM, Paavo Helde wrote:
> 20.05.2022 19:57 Christian Gollwitzer kirjutas:
>> Am 20.05.22 um 16:15 schrieb Paavo Helde:
>>> 20.05.2022 16:33 Öö Tiib kirjutas:
>>>
>>>> Modular arithmetic around likes of 42949672956 however is typically
>>>> utterly useless. 
>>> I agree in general, but I have found one use case. Suppose we have a 
>>> string s which might or might not contain a separator like ':' and we 
>>> want to either return the part after separator, or the whole string 
>>> if there is no separator. This is the code:
>>>
>>> return s.substr(s.find(':')+1);
>>>
>> And where exactly do you need mod "ridiculous number" here? WHy is 
>> that better than having a signed index variable, where negative values 
>> indicate invalid indices?
> 
> You are right, if std::string::find() returned a signed integer -1 for 
> no-find, this code would work just as fine. The only thing is that it 
> doesn't work that way, it returns size_t(-1) which is typically 
> 18446744073709551615ULL nowadays.

But your example is still valid: size_t(-1) + 1 yields the correct value 
because of unsigned wrapping, which is defined behaviour. For the rest, 
using an unsigned number as an index inside a string makes sense, 
especially in the standard library - a signed type would waste half its 
range for this use.

> 
> Coming to think of that, the only usage of unsigned types in my code 
> happens because the C++ standard library uses them for size_t and 
> related types.

[toc] | [prev] | [next] | [standalone]


#84235

FromÖö Tiib <ootiib@hot.ee>
Date2022-05-21 23:24 -0700
Message-ID<e1c90591-0995-42d3-906f-21be7fddb30cn@googlegroups.com>
In reply to#84231
On Saturday, 21 May 2022 at 22:56:22 UTC+3, Manfred wrote:
> On 5/20/2022 9:31 PM, Paavo Helde wrote: 
> > 20.05.2022 19:57 Christian Gollwitzer kirjutas: 
> >> Am 20.05.22 um 16:15 schrieb Paavo Helde: 
> >>> 20.05.2022 16:33 Öö Tiib kirjutas: 
> >>> 
> >>>> Modular arithmetic around likes of 42949672956 however is typically 
> >>>> utterly useless. 
> >>> I agree in general, but I have found one use case. Suppose we have a 
> >>> string s which might or might not contain a separator like ':' and we 
> >>> want to either return the part after separator, or the whole string 
> >>> if there is no separator. This is the code: 
> >>> 
> >>> return s.substr(s.find(':')+1); 
> >>> 
> >> And where exactly do you need mod "ridiculous number" here? WHy is 
> >> that better than having a signed index variable, where negative values 
> >> indicate invalid indices? 
> > 
> > You are right, if std::string::find() returned a signed integer -1 for 
> > no-find, this code would work just as fine. The only thing is that it 
> > doesn't work that way, it returns size_t(-1) which is typically 
> > 18446744073709551615ULL nowadays.
> But your example is still valid: size_t(-1) + 1 yields the correct value 
> because of unsigned wrapping, which is defined behaviour. For the rest, 
> using an unsigned number as an index inside a string makes sense, 
> especially in the standard library - a signed type would waste half its 
> range for this use.

The history shows that either half of that range is enough or way too short.
So where 7 bit ASCII is not enough there usage of 16 bit UCS-2 can become
obsolete decade later.

[toc] | [prev] | [next] | [standalone]


Page 1 of 2  [1] 2  Next page →

Back to top | Article view | comp.lang.c++


csiph-web