Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c++ > #84173 > unrolled thread
| Started by | Andrey Tarasevich <andreytarasevich@hotmail.com> |
|---|---|
| First post | 2022-05-18 16:17 -0700 |
| Last post | 2022-05-19 18:33 +0200 |
| Articles | 20 on this page of 39 — 14 participants |
Back to article view | Back to comp.lang.c++
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 →
| From | Andrey Tarasevich <andreytarasevich@hotmail.com> |
|---|---|
| Date | 2022-05-18 16:17 -0700 |
| Subject | Constexpr 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]
| From | "Alf P. Steinbach" <alf.p.steinbach@gmail.com> |
|---|---|
| Date | 2022-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2022-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]
| From | "Alf P. Steinbach" <alf.p.steinbach@gmail.com> |
|---|---|
| Date | 2022-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2022-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]
| From | Malcolm McLean <malcolm.arthur.mclean@gmail.com> |
|---|---|
| Date | 2022-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]
| From | Paavo Helde <eesnimi@osa.pri.ee> |
|---|---|
| Date | 2022-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]
| From | Malcolm McLean <malcolm.arthur.mclean@gmail.com> |
|---|---|
| Date | 2022-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]
| From | Juha Nieminen <nospam@thanks.invalid> |
|---|---|
| Date | 2022-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]
| From | Malcolm McLean <malcolm.arthur.mclean@gmail.com> |
|---|---|
| Date | 2022-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]
| From | Ben <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2022-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2022-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]
| From | Ben <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2022-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2022-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]
| From | Öö Tiib <ootiib@hot.ee> |
|---|---|
| Date | 2022-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]
| From | Paavo Helde <eesnimi@osa.pri.ee> |
|---|---|
| Date | 2022-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]
| From | Christian Gollwitzer <auriocus@gmx.de> |
|---|---|
| Date | 2022-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]
| From | Paavo Helde <eesnimi@osa.pri.ee> |
|---|---|
| Date | 2022-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]
| From | Manfred <noname@add.invalid> |
|---|---|
| Date | 2022-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]
| From | Öö Tiib <ootiib@hot.ee> |
|---|---|
| Date | 2022-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