But if you have just one starting point, 0 is the sensible one.
You might not like the way C handles arrays (and I'm not going to
argue about it - it certainly has its cons as well as its pros),
but even you would have to agree that defining "A[i]" to be the
element at "address of A + i * the size of the elements" is neater
and clearer than one-based indexing.
That's a crude way of defining arrays. A[i] is simply the i'th
element of N slots, you don't need to bring offsets into it.
With 0-based, there's a disconnect between the ordinal number of
the element you want, and the index that needs to be used. So A[2]
for the 3rd element.
From another thread, discussion between David and Bart:
But if you have just one starting point, 0 is the sensible one.
You might not like the way C handles arrays (and I'm not going to
argue about it - it certainly has its cons as well as its pros),
but even you would have to agree that defining "A[i]" to be the
element at "address of A + i * the size of the elements" is neater
and clearer than one-based indexing.
That's a crude way of defining arrays. A[i] is simply the i'th
element of N slots, you don't need to bring offsets into it.
Why call it 'i'th? I know people do but wouldn't it be easier to call it 'element n' where n is its index? Then that would work with any basing.
With 0-based, there's a disconnect between the ordinal number of
the element you want, and the index that needs to be used. So A[2]
for the 3rd element.
Why not call A[2] element 2?
BTW, Bart, do you consider the first ten numbers as 1 to 10 rather than
0 to 9? If so, presumably you count the hundreds as starting at 111.
That's not the most logical viewpoint.
Similarly, on the day a child is born do you say that he is one year old?
From another thread, discussion between David and Bart:
But if you have just one starting point, 0 is the sensible one.
You might not like the way C handles arrays (and I'm not going to
argue about it - it certainly has its cons as well as its pros),
but even you would have to agree that defining "A[i]" to be the
element at "address of A + i * the size of the elements" is neater
and clearer than one-based indexing.
That's a crude way of defining arrays. A[i] is simply the i'th
element of N slots, you don't need to bring offsets into it.
Why call it 'i'th? I know people do but wouldn't it be easier to call it 'element n' where n is its index? Then that would work with any basing.
With 0-based, there's a disconnect between the ordinal number of
the element you want, and the index that needs to be used. So A[2]
for the 3rd element.
Why not call A[2] element 2?
BTW, Bart, do you consider the first ten numbers as 1 to 10 rather than
0 to 9? If so, presumably you count the hundreds as starting at 111.
That's not the most logical viewpoint.
Similarly, on the day a child is born do you say that he is one year old?
BTW, Bart, do you consider the first ten numbers as 1 to 10 rather
than 0 to 9?
If so, presumably you count the hundreds as starting at
111. That's not the most logical viewpoint.
Similarly, on the day a child is born do you say that he is one year
old?
On 30/11/2021 08:07, James Harris wrote:
BTW, Bart, do you consider the first ten numbers as 1 to 10 rather
than 0 to 9?
Until quite recently, Bart and almost everyone else would
certainly have done exactly that.
Zero, as a number, was invented
in modern times [FSVO "modern"!].
"You have ten sheep and you sell
ten of them. How many sheep do you now have?" "??? I don't have
/any/ sheep left." Or, worse, "You have ten sheep and you sell
eleven of them. How many sheep do you now have?" "??? You can't
do that, it would be fraud." Or the Peano axioms for the natural
numbers: 1 is a natural number; for every n in the set, there
is a successor n' in the set; every n in the set /except/ 1 is
the successor of a unique member; .... Or look at any book;
only in a handful of rather weird books trying to make a point
is there a page 0. When you first learned to count, you almost
certainly started with a picture of a ball [or whatever] and
the caption "1 ball", then "2 cats", "3 trees", "4 cakes", ...
up to "12 candles"; not with an otherwise blank page showing
"0 children". [Note that 0 as a number in its own right is
different from the symbol 0 as a placeholder in the middle or at
the end of a number in Arabic numerals.]
Maths, inc numbers, counting, and science generally, got
along quite happily with only positive numbers from antiquity up
to around 1700, when the usefulness of the rest of the number
line became apparent, at least in maths and science if not to
the general public.
/Now/ the influence of computing has made zero-based
indexing more relevant. So have constructive arguments more
generally; eg, the surreal numbers -- a surreal number is two
sets of surreal numbers [with some conditions], so that the
natural starting point is there the two sets are empty, giving
the "empty" number, naturally identified with zero. So it was
only around 1970 that people started taking seriously the idea
of counting from zero. Of course, once you do that, then you
can contemplate not counting "from" anywhere at all; eg the
idea [which I first saw espoused by vd Meulen] that arrays
could be thought of as embedded in [-infinity..+infinity],
treated therefore always in practice as "sparse" arrays, with
almost all elements being "virtual".
On 01/12/2021 01:50, Andy Walker wrote:
On 30/11/2021 08:07, James Harris wrote:
BTW, Bart, do you consider the first ten numbers as 1 to 10 rather
than 0 to 9?
Until quite recently, Bart and almost everyone else would
certainly have done exactly that.
Remember Bart, and some others, think it is "natural" to count from
32767 on to -32767 (or larger type equivalents - 16-bit numbers are
easier to write) in the context of programming.
Clearly they would not
think that way when counting sheep. So why apply sheep counting to
other aspects of programming? Personally I prefer to think you can't
add 1 to 32767 (or larger type equivalents), which is of course almost equally silly in terms of sheep.
I am quite confident that the idea of starting array indexes from 0 had nothing to do with surreals.
Surreal numbers are rather esoteric, and
very far from useful in array indexing in programming (which always
boils down to some kind of finite integer).
On 01/12/2021 08:43, David Brown wrote:
On 01/12/2021 01:50, Andy Walker wrote:
On 30/11/2021 08:07, James Harris wrote:
BTW, Bart, do you consider the first ten numbers as 1 to 10 rather
than 0 to 9?
Until quite recently, Bart and almost everyone else would
certainly have done exactly that.
Remember Bart, and some others, think it is "natural" to count from
32767 on to -32767 (or larger type equivalents - 16-bit numbers are
easier to write) in the context of programming.
Remember David think's it's natural to count from 65535 onto 0.
I simply acknowledge that that is how most hardware works. Otherwise how
do you explain that the upper limit of some value is (to ordinary
people) the arbitrary figure of 32,767 or 65,535 instead of 99,999?
Clearly they would not
think that way when counting sheep. So why apply sheep counting to
other aspects of programming? Personally I prefer to think you can't
add 1 to 32767 (or larger type equivalents), which is of course almost
equally silly in terms of sheep.
It might be silly, but you'd still be stuck if you had 33,000 sheep to
count; what are you going to do?
I am quite confident that the idea of starting array indexes from 0 had
nothing to do with surreals.
More to do with conflating them with offsets.
Surreal numbers are rather esoteric, and
very far from useful in array indexing in programming (which always
boils down to some kind of finite integer).
a:=[:]
a{infinity} := 100
a{-infinity} := 200
println a # [Infinity:100, -Infinity:200]
[I wrote:]
Zero, as a number, was invented(Historical note:
in modern times [FSVO "modern"!].
It reached Europe around 1200, but had been around in India, amongst
other countries, for a good while before that.
The first Peano axiom is "0 is a natural number". They start counting
at zero, not at one.
There is no mathematical consensus as to whether the set of natural
numbers ℕ starts with 0 or 1. But there is no doubt that the numbers generated by the Peano axioms start at 0.
Negative numbers long pre-date the general acceptance of 0 as a
"number". They were used in accountancy, as well as by a few
mathematicians. But there general use, especially in Europe, came a
lot later.
/Now/ the influence of computing has made zero-basedI am quite confident that the idea of starting array indexes from 0 had nothing to do with surreals. [...]
indexing more relevant. So have constructive arguments more
generally; eg, the surreal numbers [...].
On 01/12/2021 08:43, David Brown wrote:
[I wrote:]
Zero, as a number, was invented(Historical note:
in modern times [FSVO "modern"!].
It reached Europe around 1200, but had been around in India, amongst
other countries, for a good while before that.
Yes, but that's nearly always zero as a placeholder, not
as a number in its own right. [I'm not convinced by many of the
claimed exceptions, which often smack of flag-waving.]
[...]
The first Peano axiom is "0 is a natural number". They start counting
at zero, not at one.
There is no mathematical consensus as to whether the set of natural
numbers ℕ starts with 0 or 1. But there is no doubt that the numbers
generated by the Peano axioms start at 0.
When Peano first wrote his axioms, he started at 1. Later
he wrote a version starting at 0. The foundational maths books on
my shelves, even modern ones, are split; it really matters very
little.
[...]
Negative numbers long pre-date the general acceptance of 0 as a
"number". They were used in accountancy, as well as by a few
mathematicians. But there general use, especially in Europe, came a
lot later.
My impression is that accountants used red ink rather than
negative numbers. As late as the 1970s, hand/electric calculators
still used red numerals rather than a minus sign.
/Now/ the influence of computing has made zero-basedI am quite confident that the idea of starting array indexes from 0 had
indexing more relevant. So have constructive arguments more
generally; eg, the surreal numbers [...].
nothing to do with surreals. [...]
Surreal numbers were an example; they are part of the
explanation for mathematics also tending to become zero-based.
On 01/12/2021 12:13, Bart wrote:
On 01/12/2021 08:43, David Brown wrote:
Remember Bart, and some others, think it is "natural" to count from
32767 on to -32767 (or larger type equivalents - 16-bit numbers are
easier to write) in the context of programming.
Remember David think's it's natural to count from 65535 onto 0.
No, I don't - as you would know if you read my posts.
I simply acknowledge that that is how most hardware works. Otherwise how
do you explain that the upper limit of some value is (to ordinary
people) the arbitrary figure of 32,767 or 65,535 instead of 99,999?
You say the limit is 32767, or whatever - explaining it in terms of the hardware if you like. People can understand that perfectly well.
Limits are quite natural in counting and measuring - wrapping is much
rarer (though it does occur, such as with times and angles).
On 30/11/2021 08:07, James Harris wrote:
From another thread, discussion between David and Bart:
That's a crude way of defining arrays. A[i] is simply the i'th
element of N slots, you don't need to bring offsets into it.
Why call it 'i'th? I know people do but wouldn't it be easier to call
it 'element n' where n is its index? Then that would work with any
basing.
The most common base I use is 1 (about 2/3 of the time). You have a
3-element array, the 1st is numbered 1, the last is 3, the 3rd is 3 too.
All very intuitive and user-friendly.
But this is that 3-element array as 3 adjoining cells:
mmmmmmmmmmmmmmmmmmmmmmmmm
m m m m
m 1 m 2 m 3 m Normal indexing
m +0 m +1 m +2 m Offsets
m m m m
mmmmmmmmmmmmmmmmmmmmmmmmm
0 1 2 3 Distance from start point
The numbering is 1, 2, 3 as I prefer when /counting/. Or you can choose
to use offsets from the first element as C does, shown as +0, +1, +2.
There is also /measuring/, which applies more when each cell has some physical dimension, such as 3 adjoining square pixels. Or maybe these
are three fence panels, and the vertical columns are the posts.
Here, offsets are again used, but notionally considered to be measured
from the first 'post'.
In this case, an 'index' of 2.4 is meaningful, being 2.4 units from the
left, and 40% along that 3rd cell.
Similarly, on the day a child is born do you say that he is one year old?
This is 'measurement'; see above. However my dad always liked to round
his age up to the next whole year; most people round down! So the child
would be 0 years, but in its first year.
However there is not enough resolution using years to accurately measure
ages of very young children, so people also use days, weeks and months.
On 2021-11-30 09:07, James Harris wrote:
From another thread, discussion between David and Bart:
That's a crude way of defining arrays. A[i] is simply the i'th
element of N slots, you don't need to bring offsets into it.
Why call it 'i'th? I know people do but wouldn't it be easier to call
it 'element n' where n is its index? Then that would work with any
basing.
You are confusing position with index. Index can be of any ordered type. Position is an ordinal number: first, second, third element from the
array beginning.
Why not call A[2] element 2?
Because it would be wrong. In most languages A[2] means the array
element corresponding to the index 2.
Remember, array is a mapping:
array : index -> element
In well-designed languages it is also spelt as a mapping:
A(2)
On 30/11/2021 09:18, Dmitry A. Kazakov wrote:
On 2021-11-30 09:07, James Harris wrote:
Why not call A[2] element 2?
Because it would be wrong. In most languages A[2] means the array
element corresponding to the index 2.
"Element 2" doesn't mean "second element"
so why is A[2] not "element 2"
just as A["XX"] would be element "XX"?
Remember, array is a mapping:
array : index -> element
In well-designed languages it is also spelt as a mapping:
A(2)
This is something I want to come back to elsewhere but since you mention
it I'm curious, Dmitry, as to whether you would accept such a mapping returning the address of, as in
A(2) = A(2) + 1
Why call it 'i'th? I know people do but wouldn't it be easier to call it >'element n' where n is its index? Then that would work with any basing.
On 30/11/2021 10:28, Bart wrote:
To explain, consider a decimal number and take the units. You may see
them as
1, 2, 3, etc
but now take the tens position. In those numbers the tens position is
zero so they can be seen as (in normal notation, not in C-form octal!)
01, 02, 03, etc
Similarly, the number of hundreds in those numbers is also zero, i.e.
with three digits they are
001, 002, 003, etc
The tens and the hundreds are each subdivided (into smaller elements
1/10th of their value). We have to wait for the units to tick past 9
before we add 1 to the tens column, and for the tens column to tick past
9 before we add another hundred. So the mathematically natural indexing
for tens and hundreds and all higher digit positions more is from zero.
It's more consistent, then, to number the units from zero, too, but we
often find it natural to count them from 1.
This is 'measurement'; see above. However my dad always liked to round
his age up to the next whole year; most people round down! So the
child would be 0 years, but in its first year.
However there is not enough resolution using years to accurately
measure ages of very young children, so people also use days, weeks
and months.
Partials, again. A person doesn't become 1 year old until he reaches 12 months, for example.
Going back to your point at the beginning, as above the ordinal of
something is naturally one more than its cardinal number. Our /first/
year is when we are age zero whole years.
In the 20th century the
century portion of the date was 19. Etc.
A[1]
as the first element even though lots of people do it. It is, in fact,
the second. It's probably easiest to refer to it as
element 1
then the bounds don't matter.
On 02/12/2021 18:05, Dmitry A. Kazakov wrote:
On 2021-12-02 18:39, James Harris wrote:
On 30/11/2021 09:18, Dmitry A. Kazakov wrote:
On 2021-11-30 09:07, James Harris wrote:
Remember, array is a mapping:
array : index -> element
In well-designed languages it is also spelt as a mapping:
A(2)
This is something I want to come back to elsewhere but since you
mention it I'm curious, Dmitry, as to whether you would accept such a
mapping returning the address of, as in
A(2) = A(2) + 1
It does not return address. A(2) denotes the array element
corresponding to the index 2 on both sides. No any addresses. A is a
mapping, mutable in this case. It does not return anything.
You as always confuse implementation with semantics. There is an
uncountable number of valid implementations of a mapping. The
programmer does not care most of the time, because he presumes the
compiler vendors are sane people, until proven otherwise.
Strange. I think it's you who too often conflates implementations with semantics. But in this case I certainly was referring at least to a
reference or ideal implentation from which information (and other
potential implementations with the same semantics) can be inferred.
But to the point, are you comfortable with the idea of the A(2) in
x = A(2) + 0
meaning the same mapping result as the A(2) in
A(2) = 0
?
On 2021-12-02 18:39, James Harris wrote:
On 30/11/2021 09:18, Dmitry A. Kazakov wrote:
On 2021-11-30 09:07, James Harris wrote:
Why not call A[2] element 2?
Because it would be wrong. In most languages A[2] means the array
element corresponding to the index 2.
"Element 2" doesn't mean "second element"
Again, A[2] is the element corresponding to the index 2. Not "element
2," not "second element," just an element denoted by the index value 2.
so why is A[2] not "element 2"
Because "element 2" is undefined, so far.
just as A["XX"] would be element "XX"?
Nope. It is the element corresponding to the index "XX".
Remember, array is a mapping:
array : index -> element
In well-designed languages it is also spelt as a mapping:
A(2)
This is something I want to come back to elsewhere but since you
mention it I'm curious, Dmitry, as to whether you would accept such a
mapping returning the address of, as in
A(2) = A(2) + 1
It does not return address. A(2) denotes the array element corresponding
to the index 2 on both sides. No any addresses. A is a mapping, mutable
in this case. It does not return anything.
You as always confuse implementation with semantics. There is an
uncountable number of valid implementations of a mapping. The programmer
does not care most of the time, because he presumes the compiler vendors
are sane people, until proven otherwise.
On 02/12/2021 17:29, James Harris wrote:
On 30/11/2021 10:28, Bart wrote:
To explain, consider a decimal number and take the units. You may see
them as
1, 2, 3, etc
but now take the tens position. In those numbers the tens position is
zero so they can be seen as (in normal notation, not in C-form octal!)
01, 02, 03, etc
Similarly, the number of hundreds in those numbers is also zero, i.e.
with three digits they are
001, 002, 003, etc
The tens and the hundreds are each subdivided (into smaller elements
1/10th of their value). We have to wait for the units to tick past 9
before we add 1 to the tens column, and for the tens column to tick
past 9 before we add another hundred. So the mathematically natural
indexing for tens and hundreds and all higher digit positions more is
from zero. It's more consistent, then, to number the units from zero,
too, but we often find it natural to count them from 1.
They're not really numbered, they're counted, and the number of tens go
from 0 to 9 in total.
I guess if you had two cars in your household, you would agree there
were '2' cars and not '1' (which would confuse everyone, and would mean
that anyone without a car would have, what, -1 cars? If doesn't work!).
But if you had to number the cars, with a number on the roof, or on the keytags, you can choose to number them 0 and 1, or 1 and 2, or 5000 and
5001, if you needed a sequential order.
The panels however do correspond to the elements of an array. This is
where I'd number them from 1 (since there is no reason to use 0 or
anything else); you'd probably use 0 for misguided reasons (perhaps too
much time spent coding in C or Python).
In the 20th century the century portion of the date was 19. Etc.
Yeah, that confuses a lot of people, but not us, right?
A[1]
as the first element even though lots of people do it. It is, in fact,
the second. It's probably easiest to refer to it as
element 1
then the bounds don't matter.
Very often you do need to refer to the first or the last. In a strictly 1-based scheme, they would be A[1] and A[N]; 0-based is A[0] and A[N-1].
X-based (since N is the length) gets ugly, eg. A.[A.lwb] and A.[A.upb]
or A[$].
However it looks you're itching to start your arrays from 0; then just
do so. You don't need an excuse.
I happen to think that 1-based is better:
* It's more intuitive and easier to understand
* It corresponds to how most discrete things are numbered in real life
* If there are N elements, the first is 1, and the last N; there is no
dis-connect are there is with 0-based
* It plays well with the rest of a language, so for-loops can go from
1 to N instead of 0 to N-1.
* In N-way select (n | a,b,c |z), then n=1/2/3 selects 1st/2nd/3rd
* If you have a list indexed 1..N, then a search function can return
1..N for success, and 0 for failure. How would it work for 0-based
since 0 could be a valid return value?
* Such a return code will also be True in conditional (if x in A then...)
But despite the advantages, I still use 0-based too; it's just not the primary choice.
On 2021-12-02 21:31, James Harris wrote:
But to the point, are you comfortable with the idea of the A(2) in
x = A(2) + 0
meaning the same mapping result as the A(2) in
A(2) = 0
?
Yes, in both cases the result is the array element corresponding to the
index 2. That is the semantics of A(2).
On 02/12/2021 20:11, Bart wrote:
As I said, whole units do not have partial, incomplete phases, and the
cars are whole units.
But if you were putting petrol in one of the cars would you count
yourself as having received a tankful when the first drop went in? No,
where elements are partial we don't count the whole until it is complete.
Similarly, if you sold one of the cars to a friend who was to pay you
£100 a month for it would you count yourself as having received the
payment after the first month? No, this is also partial so you'd count
it at the end.
Ergo it's only for indivisible units that 1-based can possibly be seen
as natural. It's more general, though, to begin counting from zero -
even if it is less familiar.
...
The panels however do correspond to the elements of an array. This is
where I'd number them from 1 (since there is no reason to use 0 or
anything else); you'd probably use 0 for misguided reasons (perhaps
too much time spent coding in C or Python).
No, I use 0 because it scales better. BTW, it sounds like the posts are
also an array.
But do you see the point of it? The first century /naturally/ had
century number zero, not one, and the N'th century has century number
N - 1
IOW the numbering begins at zero.
I happen to think that 1-based is better:
* It's more intuitive and easier to understand
It's easier on indivisible elements. That's fine if you only have a
single, simple array. But if you have arrays being processed in nested
loops then it might be best if you didn't count the outer one as
complete until the first set of iterations of the inner one have
finished. That's why I asked you before if you start numbering your three-digit numbers at 111...!
Yes, you are talking about discreet units which are not made of parts.
But despite the advantages, I still use 0-based too; it's just not the
primary choice.
Sure. For discrete units either will do - and if our programming is
mainly in discrete units then we can become accustomed to thinking
1-based. Yet that begins to run out of steam when processing hierarchies.
On 02/12/2021 20:11, Bart wrote:
On 02/12/2021 17:29, James Harris wrote:
In the 20th century the century portion of the date was 19. Etc.
Yeah, that confuses a lot of people, but not us, right?
But do you see the point of it? The first century /naturally/ had
century number zero, not one, and the N'th century has century number
N - 1
IOW the numbering begins at zero.
That's not a convention, by the way, but how all numbering works: things
with partial phases begin at zero.
On 02/12/2021 22:25, James Harris wrote:
On 02/12/2021 20:11, Bart wrote:
On 02/12/2021 17:29, James Harris wrote:
Note, however, that the first century began with year 1 AD (or 1 CE, ifIn the 20th century the century portion of the date was 19. Etc.
Yeah, that confuses a lot of people, but not us, right?
But do you see the point of it? The first century /naturally/ had
century number zero, not one, and the N'th century has century number
N - 1
IOW the numbering begins at zero.
That's not a convention, by the way, but how all numbering works: things
with partial phases begin at zero.
you prefer). The preceding year was 1 BC. There was no year 0. This
means the first century was the years 1 to 100 inclusive.
It really annoyed me that everyone wanted to celebrate the new
millennium on 01.01.2000, when in fact it did not begin until 01.01.2001.
It would have been so much simpler, and fitted people's expectations
better, if years have been numbered from 0 onwards instead of starting counting at 1.
On 03/12/2021 00:08, David Brown wrote:
On 02/12/2021 22:25, James Harris wrote:
On 02/12/2021 20:11, Bart wrote:Note, however, that the first century began with year 1 AD (or 1 CE, if
On 02/12/2021 17:29, James Harris wrote:
In the 20th century the century portion of the date was 19. Etc.
Yeah, that confuses a lot of people, but not us, right?
But do you see the point of it? The first century /naturally/ had
century number zero, not one, and the N'th century has century number
N - 1
IOW the numbering begins at zero.
That's not a convention, by the way, but how all numbering works: things >>> with partial phases begin at zero.
you prefer). The preceding year was 1 BC. There was no year 0. This
means the first century was the years 1 to 100 inclusive.
So -1 was followed by +1?
It really annoyed me that everyone wanted to celebrate the new
millennium on 01.01.2000, when in fact it did not begin until 01.01.2001.
It would have been so much simpler, and fitted people's expectations
better, if years have been numbered from 0 onwards instead of starting
counting at 1.
I'm sure we can all pretend that the start point was the year before 1
AD, which can be an honorary year 0.
It must have been a big deal on 24:00 on 31-12-999 when not only a new century began, from the 10th to the 11th, and the century year changed
not only from 9 to 10, but from 1 digit to 2 digits.
Then probably some spoilsport came along and said it didn't count, they
were still in the same century really, despite that '10' in the year,
and they'd have to wait until midnight on 31-12-1000.
On 02/12/2021 20:49, Dmitry A. Kazakov wrote:
On 2021-12-02 21:31, James Harris wrote:
...
But to the point, are you comfortable with the idea of the A(2) in
x = A(2) + 0
meaning the same mapping result as the A(2) in
A(2) = 0
?
Yes, in both cases the result is the array element corresponding to
the index 2. That is the semantics of A(2).
Cool. If A were, instead, a function that, say, ended with
return v
then what would you want those A(2)s to mean and should they still mean
the same as each other?
On 02/12/2021 20:49, Dmitry A. Kazakov wrote:
On 2021-12-02 21:31, James Harris wrote:
...
But to the point, are you comfortable with the idea of the A(2) in
x = A(2) + 0
meaning the same mapping result as the A(2) in
A(2) = 0
?
Yes, in both cases the result is the array element corresponding to
the index 2. That is the semantics of A(2).
Cool. If A were, instead, a function that, say, ended with
return v
then what would you want those A(2)s to mean and should they still mean
the same as each other? The latter expression would look strange to many.
It matters a lot once you get into the arithmetic - 0 is the additive identity.[...] But there is no doubt that the numbersWhen Peano first wrote his axioms, he started at 1. Later
generated by the Peano axioms start at 0.
he wrote a version starting at 0. The foundational maths books on
my shelves, even modern ones, are split; it really matters very
little.
I suppose you /could/ define addition with the starting point
"a + 1 = succ(a)" rather than "a + 0 = a", but it is all much easier and neater when you start with 0.
That is certainly how I learned it at
university, and how I have seen it a few other places - but while I
think I have a couple of books covering them, they are buried in the
attic somewhere.
Really? Again, I would suggest that they are far too esoteric for the purpose.I am quite confident that the idea of starting array indexes from 0 hadSurreal numbers were an example; they are part of the
nothing to do with surreals.
explanation for mathematics also tending to become zero-based.
Constructions of surreal numbers will normally start with 0 -
but so will constructions of other more familiar types, such as
integers, reals, ordinals, cardinals, and almost any other numbers.
Maybe it is just that with surreals, few people ever have much idea of
what they are, or get beyond reading how they are constructed! (Some
day I must get the book on them - it was Conway that developed them, and Knuth that wrote the book, right?)
On 02/12/2021 07:37, David Brown wrote:
It matters a lot once you get into the arithmetic - 0 is the additive[...] But there is no doubt that the numbersWhen Peano first wrote his axioms, he started at 1. Later
generated by the Peano axioms start at 0.
he wrote a version starting at 0. The foundational maths books on
my shelves, even modern ones, are split; it really matters very
little.
identity.
"Additive identity" is meaningless before you have defined addition; and once you have got to anywhere interesting, "0" is
defined anyway. It's really not important, except when you get to
rationals [when 1-based is better, as you don't have to make a
special case for when the denominator is zero].
I suppose you /could/ define addition with the starting point
"a + 1 = succ(a)" rather than "a + 0 = a", but it is all much easier and
neater when you start with 0.
I don't see "a + 1 == a'" as interestingly harder than
"a + 0 = a". In some ways it's easier; if we abbreviate [eg]
3' [or succ(3)] as 4, in the usual way, then 1-based has [eg]
3+4 = (3+3)' = ((3+2)')' = (((3+1)')')' = 4''' = 5'' = 6' = 7,
whereas 0-based has
3+4 = (3+3)' = ((3+2)')' = (((3+1)')')' = ((((3+0)')')')' =
3'''' = 4''' = 5'' = 6' = 7,
and you have two extra steps with every addition.
That is certainly how I learned it at
university, and how I have seen it a few other places - but while I
think I have a couple of books covering them, they are buried in the
attic somewhere.
Fine; as I said, books vary, and it's at the whim of
the lecturer [if any -- it's commonly not taught at all, except
at the level of "if you really want to know how all this stuff
gets defined, look at (some book -- Landau in my case)"].
[...]
Really? Again, I would suggest that they are far too esoteric for theI am quite confident that the idea of starting array indexes from 0 had >>>> nothing to do with surreals.Surreal numbers were an example; they are part of the
explanation for mathematics also tending to become zero-based.
purpose.
Again, I would repeat that they were an /example/ of the
way that /mathematics/, predominantly 1-based, has /tended/ to
become 0-based. That's not a /purpose/; it just so happens that
some relatively recent maths has found uses where 0-based seems
more natural than 1-based. There are still plenty where 1-based
remains more usual/natural.
Constructions of surreal numbers will normally start with 0 - >> but so will constructions of other more familiar types, such as
integers, reals, ordinals, cardinals, and almost any other numbers.
You're assuming the answer! As above, you can equally
get to integers [and so rationals and reals] from 1.
Maybe it is just that with surreals, few people ever have much idea of
what they are, or get beyond reading how they are constructed! (Some
day I must get the book on them - it was Conway that developed them, and
Knuth that wrote the book, right?)
Knuth wrote /a/ book on them; /the/ book is Conway's "On
Numbers and Games", but a more accessible version is "Winning Ways"
by Berlekamp, Conway and Guy [all three of whom, sadly, died within
a year and two days in 2019-20]; expensive to buy, but there is a
PDF freely available online.
What most people don't realise is the
motivation: Conway couldn't see /why/ the step from rationals to
reals is so difficult. We define naturals eg by Peano, then get
by equivalence classes to integers and rationals, and then ...?
The usual constructions of reals seem so artificial, and not at
all related to what happens earlier. So Conway wondered what
would happen if we went the other way -- start from the concept
of a Dedekind section, forget that it relies on knowing about
the rationals, and just build on what we know. Thus we get the
idea of partitioning whatever numbers we know into two sets.
That is how we build the surreals, without exceptions or special
cases.
Oh, we also get [combinatorial] games as a side-effect;
which is where it gets interesting to people like me, and to CS
more generally, and why it's not as esoteric as people think.
Rationals are easier when you have 0 :
ℚ = { p / q : p, q ∈ ℤ, q > 0 }
vs.
ℚ = { p / q : p, q ∈ ℕ⁺ } ∪ { 0 } ∪ { -p / q : p, q ∈ ℕ⁺ }
[...] But calling the additive identity "0" or
"1" /does/ matter, because one choice makes sense and the other choice
is pointlessly confusing. (The definition of addition does not actually require an identity.)
[...] In my experience (as someone
with a degree in mathematics and theoretical computing, [...].
I had thought of surreals as trying to find gaps
and endpoints in the reals and filling them in (though I know the construction doesn't do that).
I wonder if the lengths in TeX, which have 3 (IIRC) layers of infinities
and infinitesimals, were invented as a kind of computer approximation to surreals?
All sorts of maths that is esoteric to most people has some odd
application or two. That is often what makes it interesting.
On 03/12/2021 14:38, David Brown wrote:
Rationals are easier when you have 0 :
ℚ = { p / q : p, q ∈ ℤ, q > 0 }
vs.
ℚ = { p / q : p, q ∈ ℕ⁺ } ∪ { 0 } ∪ { -p / q : p, q ∈ ℕ⁺ }
Or vs ℚ = { p, q : p ∈ ℤ, q ∈ ℕ }.
[...] But calling the additive identity "0" or
"1" /does/ matter, because one choice makes sense and the other choice
is pointlessly confusing. (The definition of addition does not actually
require an identity.)
No-one calls the additive identity 1, esp as it's not even
useful until you get to much more advanced maths, by which time you
already have 0 [part of ℤ, defined by equivalence classes of pairs
of members of ℕ].
[...] In my experience (as someone
with a degree in mathematics and theoretical computing, [...].
No such thing when I was a student!
I had thought of surreals as trying to find gaps
and endpoints in the reals and filling them in (though I know the
construction doesn't do that).
No, rather that "filling in gaps" [ie, partitioning known
numbers] produces the reals and more. If the partitioning is
ordered, you get numbers; if unordered, you get games [of which
numbers are therefore a subset].
I wonder if the lengths in TeX, which have 3 (IIRC) layers of infinities
and infinitesimals, were invented as a kind of computer approximation to
surreals?
Pass. I've never used [and don't like] TeX; we had real
experts in typography in my dept [consultants for major academic
publishers, exam boards, etc], and some of that rubbed off. They
disliked the "look" of Knuth's books, and even more so that we
kept being told that TeX knows best. They devoted their time to
tweaking Troff, which takes a more pragmatic view [and some of
the tweaks found their way back into "official" Troff]. I also
quite like Lout, FWIW.
[...]
All sorts of maths that is esoteric to most people has some odd
application or two. That is often what makes it interesting.
/All/ maths is esoteric to most people! A large majority
don't even know that there is maths beyond arithmetic, apart from
the algebra [etc] that never made any sense at all to them.
ℕ is ambiguous - you need to write something like ℕ⁺ unless it is clear from earlier.
While it is quite easy to write ℕ⁺, there is no good
argument for suggesting it is noticeably simpler than using ℤ, nor any special case handling for 0.
You wrote:
I don't see "a + 1 == a'" as interestingly harder than
"a + 0 = a". In some ways it's easier;
Presumably then that was a typo. (Fair enough, we all do that.)
On 02/12/2021 01:11, Andy Walker wrote:
[...]
Negative numbers long pre-date the general acceptance of 0 as a
"number". They were used in accountancy, as well as by a few
mathematicians. But there general use, especially in Europe, came a
lot later.
My impression is that accountants used red ink rather than
negative numbers. As late as the 1970s, hand/electric calculators
still used red numerals rather than a minus sign.
Many conventions have been used, in different countries, times, and
cultures. "Red ink" is certainly a well-known phrase in modern English-speaking countries. But brackets, minus signs, and other
methods are used. Go far enough back and people didn't write with ink
at all.
On 03/12/2021 17:49, David Brown wrote:
[Definition of rationals:]
ℕ is ambiguous - you need to write something like ℕ⁺ unless it is clear
from earlier.
It was clear in context! But ℕ⁺ is still a more primitive concept than ℤ.
While it is quite easy to write ℕ⁺, there is no good
argument for suggesting it is noticeably simpler than using ℤ, nor any
special case handling for 0.
My version didn't have any special casing for 0. Note that according to the usual development you need four members of ℕ to
construct two members of ℤ [as two equivalence classes of members
of ℕ], of which one is logically redundant.
You wrote:
I don't see "a + 1 == a'" as interestingly harder than
"a + 0 = a". In some ways it's easier;
Presumably then that was a typo. (Fair enough, we all do that.)
If it was, I still don't see it. Did you miss the "'"
[usual abbreviation for the successor function]?
I doubt whether I have anything further to contribute
to this thread, which is now diverging a long way from CS.
From another thread, discussion between David and Bart:
But if you have just one starting point, 0 is the sensible one.
You might not like the way C handles arrays (and I'm not going to
argue about it - it certainly has its cons as well as its pros),
but even you would have to agree that defining "A[i]" to be the
element at "address of A + i * the size of the elements" is neater
and clearer than one-based indexing.
That's a crude way of defining arrays. A[i] is simply the i'th
element of N slots, you don't need to bring offsets into it.
Why call it 'i'th? I know people do but wouldn't it be easier to call
it 'element n' where n is its index? Then that would work with any
basing.
'element n'
With 0-based, there's a disconnect between the ordinal number of
the element you want, and the index that needs to be used. So A[2]
for the 3rd element.
Why not call A[2] element 2?
BTW, Bart, do you consider the first ten numbers as 1 to 10 rather
than 0 to 9?
[What does Rod prefer?]
(Let's hope I'm not talking to myself now.)
On Tue, 30 Nov 2021 08:07:30 +0000
James Harris <james.harris.1@gmail.com> wrote:
[What does Rod prefer?]
(Let's hope I'm not talking to myself now.)
0-based works really well with C,
"unnatural" at first, even though zero was the central starting point of
the signed number line in mathematics.
For programming, I strongly
prefer unsigned only. This eliminates many coding errors.
E.g., for C, you can do neat 0-based "tricks" like below for loops to
detect loop termination. I.e., the value of 10 below as MAX detects the end-of-loop of the 10 values of variable "i" with 0..9 being the actual printed values:
#define MAX 10
for(i=0;i!=MAX;i++)
And, if the language is designed correctly and the variable is declared
with file scope, using zero-based indexing means that variables don't
need to be initialized to zero. They'll be cleared to zero upon
program execution. I.e., the "i=0" above should be optional in a
language for all declared file scope variables, since such variables
should be initialized to or cleared to zero by default.
Maybe I've been coding in C for too long now, as I prefer zero-based indexing, even in non-C situations like personal to-do lists.
0-based works really well with C, but I do recall it being somewhat "unnatural" at first, even though zero was the central starting point of
the signed number line in mathematics. For programming, I strongly
prefer unsigned only. This eliminates many coding errors.
E.g., for C, I know that the address of the 0'th (zeroth) element of an "array" (&A[0]) is the same as the base address of the said "array"
named A. This can be convenient as no address calculation needs to be computed because there is no actual indexing into the array when the
index is zero.
E.g., for C, you can do neat 0-based "tricks" like below for loops to
detect loop termination. I.e., the value of 10 below as MAX detects the end-of-loop of the 10 values of variable "i" with 0..9 being the actual printed values:
#define MAX 10
for(i=0;i!=MAX;i++)
And, if the language is designed correctly and the variable is declared
with file scope, using zero-based indexing means that variables don't
need to be initialized to zero. They'll be cleared to zero upon
program execution. I.e., the "i=0" above should be optional in a
language for all declared file scope variables, since such variables
should be initialized to or cleared to zero by default. E.g., a C
compiler may warn if "i=0" initialization is missing when "i" is
declared with auto or local scope (within a procedure), but C compilers
will generally not warn when "i" is declared with file or global scope
(or local static) as they are initialized to zero due to BSS.
On 06/12/2021 11:41, Rod Pemberton wrote:
Maybe I've been coding in C for too long now, as I prefer zero-based indexing, even in non-C situations like personal to-do lists.
0-based works really well with C, but I do recall it being somewhat "unnatural" at first, even though zero was the central starting
point of the signed number line in mathematics. For programming, I strongly prefer unsigned only. This eliminates many coding errors.
Out of curiosity, what kinds of coding errors do you eliminate by
using unsigned types?
E.g., for C, I know that the address of the 0'th (zeroth) element
of an "array" (&A[0]) is the same as the base address of the said
"array" named A. This can be convenient as no address calculation
needs to be computed because there is no actual indexing into the
array when the index is zero.
E.g., for C, you can do neat 0-based "tricks" like below for loops
to detect loop termination. I.e., the value of 10 below as MAX
detects the end-of-loop of the 10 values of variable "i" with 0..9
being the actual printed values:
#define MAX 10
for(i=0;i!=MAX;i++)
And, if the language is designed correctly and the variable is
declared with file scope, using zero-based indexing means that
variables don't need to be initialized to zero. They'll be cleared
to zero upon program execution. I.e., the "i=0" above should be
optional in a language for all declared file scope variables, since
such variables should be initialized to or cleared to zero by
default. E.g., a C compiler may warn if "i=0" initialization is
missing when "i" is declared with auto or local scope (within a
procedure), but C compilers will generally not warn when "i" is
declared with file or global scope (or local static) as they are initialized to zero due to BSS.
Loop variables are almost always local, rather than file scope (or
other static lifetime). Indeed, they are normally local to the loop
itself. The idiomatic for loop in C is :
for (int i = 0; i < MAX; i++) { ... }
On 06/12/2021 10:41, Rod Pemberton wrote:
On Tue, 30 Nov 2021 08:07:30 +0000
James Harris <james.harris.1@gmail.com> wrote:
[What does Rod prefer?]
(Let's hope I'm not talking to myself now.)
0-based works really well with C,
Well, C invented it. (If it didn't, then it made it famous.)
but I do recall it being somewhat
"unnatural" at first, even though zero was the central starting
point of the signed number line in mathematics.
Yeah, this my fence/fencepost distinction.
If you draw XY axes on squared paper, and start annotating the
positive X axis as 0 (at Y-axis), 1, 2, 3 ..., then those figures
will mark the vertical divisions between the squares.
NOT the squares themselves. The squares are what correspond to C
array elements.
For programming, I strongly
prefer unsigned only. This eliminates many coding errors.
OK, so we can forget about that negative X axis!
E.g., for C, you can do neat 0-based "tricks" like below for loops
to detect loop termination. I.e., the value of 10 below as MAX
detects the end-of-loop of the 10 values of variable "i" with 0..9
being the actual printed values:
#define MAX 10
for(i=0;i!=MAX;i++)
And, if the language is designed correctly and the variable is
declared with file scope, using zero-based indexing means that
variables don't need to be initialized to zero. They'll be cleared
to zero upon program execution. I.e., the "i=0" above should be
optional in a language for all declared file scope variables, since
such variables should be initialized to or cleared to zero by
default.
That's great. Until the second time you execute this loop:
for(; i!=MAX; i++)
since now i will have value 10 (or whatever it ended up as after 1000
more lines of code, being a file-scope variable visible from any
function). Or when you execute this separate loop further on:
for(; i<N; i++)
On Mon, 6 Dec 2021 14:56:41 +0100
David Brown <david.brown@hesbynett.no> wrote:
On 06/12/2021 11:41, Rod Pemberton wrote:
Maybe I've been coding in C for too long now, as I prefer zero-based
indexing, even in non-C situations like personal to-do lists.
0-based works really well with C, but I do recall it being somewhat
"unnatural" at first, even though zero was the central starting
point of the signed number line in mathematics. For programming, I
strongly prefer unsigned only. This eliminates many coding errors.
Out of curiosity, what kinds of coding errors do you eliminate by
using unsigned types?
When you expect the variable to function as unsigned, which is common
in C when working with characters, pointers, or binary data, but the
variable was actually declared as signed. If you add the value to
another integer, you'll end up with the "wrong" numeric result, i.e.,
other than what was expected because the variable was signed.
i=1000
x=0xFF (signed 8-bit char)
y=0xFF (unsigned 8-bit char)
i+x = 999 (unexpected)
i+y = 1255 (expected)
On Mon, 6 Dec 2021 14:56:41 +0100
David Brown <david.brown@hesbynett.no> wrote:
On 06/12/2021 11:41, Rod Pemberton wrote:
Maybe I've been coding in C for too long now, as I prefer zero-based
indexing, even in non-C situations like personal to-do lists.
0-based works really well with C, but I do recall it being somewhat
"unnatural" at first, even though zero was the central starting
point of the signed number line in mathematics. For programming, I
strongly prefer unsigned only. This eliminates many coding errors.
Out of curiosity, what kinds of coding errors do you eliminate by
using unsigned types?
When you expect the variable to function as unsigned, which is common
in C when working with characters, pointers, or binary data, but the
variable was actually declared as signed.
If you add the value to
another integer, you'll end up with the "wrong" numeric result, i.e.,
other than what was expected because the variable was signed.
i=1000
x=0xFF (signed 8-bit char)
y=0xFF (unsigned 8-bit char)
i+x = 999 (unexpected)
i+y = 1255 (expected)
E.g., for C, I know that the address of the 0'th (zeroth) element
of an "array" (&A[0]) is the same as the base address of the said
"array" named A. This can be convenient as no address calculation
needs to be computed because there is no actual indexing into the
array when the index is zero.
E.g., for C, you can do neat 0-based "tricks" like below for loops
to detect loop termination. I.e., the value of 10 below as MAX
detects the end-of-loop of the 10 values of variable "i" with 0..9
being the actual printed values:
#define MAX 10
for(i=0;i!=MAX;i++)
And, if the language is designed correctly and the variable is
declared with file scope, using zero-based indexing means that
variables don't need to be initialized to zero. They'll be cleared
to zero upon program execution. I.e., the "i=0" above should be
optional in a language for all declared file scope variables, since
such variables should be initialized to or cleared to zero by
default. E.g., a C compiler may warn if "i=0" initialization is
missing when "i" is declared with auto or local scope (within a
procedure), but C compilers will generally not warn when "i" is
declared with file or global scope (or local static) as they are
initialized to zero due to BSS.
Loop variables are almost always local, rather than file scope (or
other static lifetime). Indeed, they are normally local to the loop
itself. The idiomatic for loop in C is :
for (int i = 0; i < MAX; i++) { ... }
The paragraph on clearing variables, wasn't intended to be specifically linked with the concept of a loop variable, or even a discussion of C specifically, but was a generic statement for any file scope or global variables, for some new language using C as a means to explain.
The other option, per the prior discussion, would require that the file
scope variables in BSS all be "cleared" to a value of one. As you know
from C, the representations for a value of one may be different for
each type of variable, e.g., integer vs float.
As for C, variable declarations within the for() loop is not valid
for ANSI C (C89), i.e., valid for C99 or C11 or later.
So, one could
argue, that to ensure backwards code compatibility, hence portability
of C code, that declaring a variable somewhere within a procedure, such
as within a for() loop, should be avoided, yes? Think of C style guide suggestions.
On 07/12/2021 12:52, Rod Pemberton wrote:
On Mon, 6 Dec 2021 14:56:41 +0100
David Brown <david.brown@hesbynett.no> wrote:
On 06/12/2021 11:41, Rod Pemberton wrote:
Maybe I've been coding in C for too long now, as I prefer zero-based
indexing, even in non-C situations like personal to-do lists.
0-based works really well with C, but I do recall it being somewhat
"unnatural" at first, even though zero was the central starting
point of the signed number line in mathematics. For programming, I
strongly prefer unsigned only. This eliminates many coding errors.
Out of curiosity, what kinds of coding errors do you eliminate by
using unsigned types?
When you expect the variable to function as unsigned, which is common
in C when working with characters, pointers, or binary data, but the
variable was actually declared as signed. If you add the value to
another integer, you'll end up with the "wrong" numeric result, i.e.,
other than what was expected because the variable was signed.
i=1000
x=0xFF (signed 8-bit char)
If x has int8 type then it has the value -1 not +255.
y=0xFF (unsigned 8-bit char)
i+x = 999 (unexpected)
This is not unexpected, only if you expect int8 to be able to represent
+255.
i+y = 1255 (expected)
Unsigned has its own problems:
unsigned int i=1000;
unsigned int x=1001;
printf("%u\n",i-x);
printf("%f\n",(double)(i-x));
displsys:
4294967295
4294967295.000000
On 07/12/2021 15:05, Bart wrote:
Unsigned has its own problems:
unsigned int i=1000;
unsigned int x=1001;
printf("%u\n",i-x);
printf("%f\n",(double)(i-x));
displsys:
4294967295
4294967295.000000
That is also not unexpected.
(Other languages could reasonably handle this differently - C's
treatment of unsigned int as a modulo type is not the only practical way
to handle things. But if you know the basics of C, it is neither a
problem not a surprise.)
On 07/12/2021 14:55, David Brown wrote:
On 07/12/2021 15:05, Bart wrote:
Unsigned has its own problems:
unsigned int i=1000;
unsigned int x=1001;
printf("%u\n",i-x);
printf("%f\n",(double)(i-x));
displsys:
4294967295
4294967295.000000
That is also not unexpected.
(Other languages could reasonably handle this differently - C's
treatment of unsigned int as a modulo type is not the only practical way
to handle things. But if you know the basics of C, it is neither a
problem not a surprise.)
When using signed integers, then you really only get problems with
overflows and such at quite high magnitudes, around +/- 2e9 for int32,
and considerably higher for int64 at +/- 9e18.
But you will normally work with values a long way from those limits,
unless results are stored in narrower types, so such problems are rare.
With unsigned numbers however, one of those problematic limits is zero,
which is really too close for comfort! So you will see problems even
with small, very ordinary calculations, such as 2 - 3, which here
underflows that zero.
On 08/12/2021 01:42, Bart wrote:
On 07/12/2021 14:55, David Brown wrote:
On 07/12/2021 15:05, Bart wrote:
Unsigned has its own problems:
unsigned int i=1000;
unsigned int x=1001;
printf("%u\n",i-x);
printf("%f\n",(double)(i-x));
displsys:
4294967295
4294967295.000000
That is also not unexpected.
(Other languages could reasonably handle this differently - C's
treatment of unsigned int as a modulo type is not the only practical way >>> to handle things. But if you know the basics of C, it is neither a
problem not a surprise.)
When using signed integers, then you really only get problems with
overflows and such at quite high magnitudes, around +/- 2e9 for int32,
and considerably higher for int64 at +/- 9e18.
Yes.
But you will normally work with values a long way from those limits,
unless results are stored in narrower types, so such problems are rare.
Agreed.
With unsigned numbers however, one of those problematic limits is zero,
which is really too close for comfort! So you will see problems even
with small, very ordinary calculations, such as 2 - 3, which here
underflows that zero.
Disagreed.
Whatever kind of numbers you use, you have to apply a few brain cells.
You can't represent 1/3 with an integer, no matter how big it is. You
can't represent negative numbers with unsigned types. It's common
sense, not a "problematic limit". Anyone who finds it surprising that
you can't subtract 3 from 2 without signed numbers should give up their programming career and go back to primary school. We have to have
/some/ standard of education in this profession!
On 08/12/2021 08:41, David Brown wrote:
On 08/12/2021 01:42, Bart wrote:
On 07/12/2021 14:55, David Brown wrote:
On 07/12/2021 15:05, Bart wrote:
Unsigned has its own problems:
unsigned int i=1000;
unsigned int x=1001;
printf("%u\n",i-x);
printf("%f\n",(double)(i-x));
displsys:
4294967295
4294967295.000000
That is also not unexpected.
(Other languages could reasonably handle this differently - C's
treatment of unsigned int as a modulo type is not the only practical
way
to handle things. But if you know the basics of C, it is neither a
problem not a surprise.)
When using signed integers, then you really only get problems with
overflows and such at quite high magnitudes, around +/- 2e9 for int32,
and considerably higher for int64 at +/- 9e18.
Yes.
But you will normally work with values a long way from those limits,
unless results are stored in narrower types, so such problems are rare.
Agreed.
With unsigned numbers however, one of those problematic limits is zero,
which is really too close for comfort! So you will see problems even
with small, very ordinary calculations, such as 2 - 3, which here
underflows that zero.
Disagreed.
Whatever kind of numbers you use, you have to apply a few brain cells.
You can't represent 1/3 with an integer, no matter how big it is. You
can't represent negative numbers with unsigned types. It's common
sense, not a "problematic limit". Anyone who finds it surprising that
you can't subtract 3 from 2 without signed numbers should give up their
programming career and go back to primary school. We have to have
/some/ standard of education in this profession!
It won't be 2 - 3, it will be A - B.
Can the result be properly represented, or not? Given ordinary (ie.
smallish) signed values, it can. Given ordinary unsigned values, the
chances are 50% that it can't!
This is why I prefer signed types for general use to unsigned types. And
why my mixed arithmetic is performed using signed types.
Imagine working with unsigned float; where would you start with all the potential problems!
C of course prefers to use unsigned for mixed arithmetic (although the precise rules are complex). So here:
int a = 2;
unsigned b = 3;
double c = a-b;
printf("%f\n", c);
it prints 4294967295.000000. Same using b-4 instead of a-b.
If I do the same:
int a := 2
word b := 3
real c := a-b
println c
it shows -1.000000, for b-4 too. Fewer surprises.
I actually do all arithmetic using at least i64. Values of types u8, u16
and u32 are converted losslessly to i64 first. It's only when u64 is
involved that you need to start taking care, but my example uses u64 ('word'), and that has more sensible behaviour than the C.
On 08/12/2021 10:45, Bart wrote:
C of course prefers to use unsigned for mixed arithmetic (although the
precise rules are complex). So here:
The precise rules are simple, not complex. Pretending they are
difficult does not help.
Personally, however, I don't think they are
good - I would have preferred rules that promoted both sides to a common
type that is big enough for all the values involved. Thus "signed int + unsigned int" should be done as "signed long" (or "signed long long" if necessary - or the rules for sizes should be changed too). Failing
that, it should be a constraint error (i.e., fail to compile).
C doesn't give me the rules I want here,
In particular, what does your language give for :
int a := 2
int b := 3
real c := b / a;
println c
Does it print 1, or 1.5 ?
The C version would give 1. Ada, as far as I could see in a quick test
on <https://godbolt.org>, will not accept mixing types in the same
expression or assignment without explicit casts.
On 08/12/2021 11:07, David Brown wrote:
On 08/12/2021 10:45, Bart wrote:
C of course prefers to use unsigned for mixed arithmetic (although the
precise rules are complex). So here:
The precise rules are simple, not complex. Pretending they are
difficult does not help.
Here is the table of rules for C: S means the operation is performed as signed with a signed result; "." (chosen to make it clearer) means
unsigned:
u8 u16 u32 u64 i8 i16 i32 i64
u8 S S . . S S S S
u16 S S . . S S S S
u32 . . . . . . . S
u64 . . . . . . . .
i8 S S . . S S S S
i16 S S . . S S S S
i32 S S . . S S S S
i64 S S S . S S S S
Here is the corresponding table for my language:
u8 u16 u32 u64 i8 i16 i32 i64
u8 . . . . S S S S
u16 . . . . S S S S
u32 . . . . S S S S
u64 . . . . S S S S
i8 S S S S S S S S
i16 S S S S S S S S
i32 S S S S S S S S
i64 S S S S S S S S
I think people can make up their own minds as to which has the simpler
rules!
On 08/12/2021 11:07, David Brown wrote:
On 08/12/2021 10:45, Bart wrote:
C of course prefers to use unsigned for mixed arithmetic (although the
precise rules are complex). So here:
The precise rules are simple, not complex. Pretending they are
difficult does not help.
In particular, what does your language give for :
int a := 2
int b := 3
real c := b / a;
println c
Does it print 1, or 1.5 ?
My languages have two divide operators: "/" and "%".
"%" means integer divide. "/" is supposed to be for floating point
divide, but that's only on one language; the static one will still do
integer divide when both operands are integers.
So M will give 1.0, Q will give 1.5.
On 08/12/2021 15:36, David Brown wrote:
On 08/12/2021 12:55, Bart wrote:
What is it with you and your campaign to claim everything C is bad, and
everything in your useless little private language is good?
I said the rules are complex. You said they are simple. I disagreed, and illustrated my point with a chart.
than "int" is first converted to an "int". Then if the two parts have
different types, they are converted to the bigger type with "unsigned"
types being treated as slightly bigger than the signed types.
At least, they are simpler than the rules for type syntax. And not much simpler than the rules for charting the Mandelbrot Set!
It is /not/ hard. It is /not/ complex. You might not think it is
ideal, and I'd agree. But it really is not rocket science, and it
doesn't need a complicated table of inappropriate made-up types
What made-up types? And why are they inappropriate?
Are you sure you aren't twisting and making up things yourself?
to make
it look more complicated.
I think most people would be surprised at how untidy that chart is. /I/
was.
Does it print 1, or 1.5 ?
My languages have two divide operators: "/" and "%".
"%" means integer divide. "/" is supposed to be for floating point
divide, but that's only on one language; the static one will still do
integer divide when both operands are integers.
Genius. Does it also use "and" as a keyword for the remainder after
division? Nothing says "simple" and "intuitive" like picking different
meanings for your operators than all other languages.
"%" was used for integer divide in Pascal. I adopted it in the 1980s
when I needed distinct operators.
And I use "rem" for integer REMainder instead of "%"; "ixor" instead of
"^"; "ior" instead of "|" and "or" instead of "||". Maybe it's just me,
but I find them more readable.
Why, what do other languages use for integer divide?
So M will give 1.0, Q will give 1.5.
That's your two languages that are proudly the same syntax, but handle
expressions in completely different ways?
Funnily enough, C and Python will also give 1.0 and 1.5 respectively.
But that of course is fine.
On 08/12/2021 12:55, Bart wrote:
What is it with you and your campaign to claim everything C is bad, and everything in your useless little private language is good?
than "int" is first converted to an "int". Then if the two parts have different types, they are converted to the bigger type with "unsigned"
types being treated as slightly bigger than the signed types.
It is /not/ hard. It is /not/ complex. You might not think it is
ideal, and I'd agree. But it really is not rocket science, and it
doesn't need a complicated table of inappropriate made-up types
to make
it look more complicated.
Does it print 1, or 1.5 ?
My languages have two divide operators: "/" and "%".
"%" means integer divide. "/" is supposed to be for floating point
divide, but that's only on one language; the static one will still do
integer divide when both operands are integers.
Genius. Does it also use "and" as a keyword for the remainder after division? Nothing says "simple" and "intuitive" like picking different meanings for your operators than all other languages.
So M will give 1.0, Q will give 1.5.
That's your two languages that are proudly the same syntax, but handle expressions in completely different ways?
On 08/12/2021 17:58, Bart wrote:
On 08/12/2021 15:36, David Brown wrote:
On 08/12/2021 12:55, Bart wrote:
What is it with you and your campaign to claim everything C is bad, and
everything in your useless little private language is good?
I said the rules are complex. You said they are simple. I disagreed, and
illustrated my point with a chart.
A chart designed purely to make the simple rules of C appear complex -
it is FUD. You added those of your own language, which is utterly
irrelevant to C, purely to be able to claim that the rules of your
language are simple.
What made-up types? And why are they inappropriate?
There are no types of the names you used in C. C has a perfectly good
set of fundamental types (regardless of what you personally might think
of them, or even what /I/ personally might think of them), and the rules
of C are given in terms of those types.
Are you sure you aren't twisting and making up things yourself?
to make
it look more complicated.
I think most people would be surprised at how untidy that chart is. /I/
was.
You seem to find just about everything in C surprising.
But let's be clear here. Do you think people familiar and experienced
with C programming will find C's rules surprising
Or do you just think
people who have never used C will find them surprising?
Why, what do other languages use for integer divide?
Most use /.
And in most languages, if they have % operator for
integers, it means modulus.
On 08/12/2021 12:55, Bart wrote:
On 08/12/2021 11:07, David Brown wrote:
On 08/12/2021 10:45, Bart wrote:
C of course prefers to use unsigned for mixed arithmetic (although the >>>> precise rules are complex). So here:
The precise rules are simple, not complex. Pretending they are
difficult does not help.
What is it with you and your campaign to claim everything C is bad, and everything in your useless little private language is good? It doesn't matter what anyone writes - you /always/ twist the facts, move the
goalposts or deliberately misinterpret what others write. (And yes,
your language is useless - no one else will ever use it. You've had
made useful software with it and used it in your work in the past.
That's great, and genuinely praise-worthy. But it is dead now. Move
along.)
So - let's start with some kindergarten logic. Claiming that your rules
are simpler than C's does not make C's rules complex.
In a binary arithmetic expression with integer types, any type smaller
than "int" is first converted to an "int". Then if the two parts have different types, they are converted to the bigger type with "unsigned"
types being treated as slightly bigger than the signed types.
It is /not/ hard. It is /not/ complex. You might not think it is
ideal, and I'd agree. But it really is not rocket science, and it
doesn't need a complicated table of inappropriate made-up types to make
it look more complicated.
Oh, and your method will screw up too, for some cases. /Any/ method
will in some cases, unless you have unlimited ranges for your integers
(like Python) or point-blank refuse mixed signed expressions (like Ada).
And your language will still screw up on overflows.
(And before you post your knee-jerk response, the fact that C gets
things wrong on overflow does not mean your language is right or better.)
<snip more pointless and annoying drivel>
In particular, what does your language give for :
int a := 2
int b := 3
real c := b / a;
println c
Does it print 1, or 1.5 ?
My languages have two divide operators: "/" and "%".
"%" means integer divide. "/" is supposed to be for floating point
divide, but that's only on one language; the static one will still do
integer divide when both operands are integers.
Genius. Does it also use "and" as a keyword for the remainder after division? Nothing says "simple" and "intuitive" like picking different meanings for your operators than all other languages.
So M will give 1.0, Q will give 1.5.
That's your two languages that are proudly the same syntax, but handle expressions in completely different ways?
If you want to keep posting about your own language, please feel free -
only you can tell if you are making things up as you go along. But
/please/ stop posting shite about other languages that you refuse to understand.
Understand me correctly here - I really don't care if you like C or not.
I don't care if anyone else here likes it or not, uses it or not. I am
not interested in promoting C or any other language - I'll use what I
want to use, and others will use what they want.
But what I /do/ react against is lies, FUD, and misrepresentations. I
am not "pro-C" - I am "anti-FUD", and it just so happens that your
bizarre hatred of C means it is C you post rubbish about.
I'd react
against anyone else deliberately and repeatedly writing nonsense about
other topics too.
I post criticisms of quite a few languages I come across, although in
this group it might be largely C and Algol68 that come up.
C figures highly because I can't really get away from it; it's
everywhere. It's also the one whose purpose and use-cases most closely
match my own.
But it also annoys me that it is so deified despite being a such a
dreadful language.
That is not surprising given when it was created, nearly 50 years ago.
But it hasn't moved on. Its aficionados seem to treat every misfeature
as an advantage.
I'd react
against anyone else deliberately and repeatedly writing nonsense about
other topics too.
You mention lots of things you don't like about C. But it sounds like
you don't have much of a choice about it; you have to rely on external
tools to make it useful. That's OK, many people are stuck with languages
they don't like.
But some of us can do something about it, yet that seems to annoy you
and you are constantly belittling people's efforts, especially mine.
On 07/12/2021 13:52, Rod Pemberton wrote:
As for C, variable declarations within the for() loop is not validC18 is the current standard - without other qualifications, that is what
for ANSI C (C89), i.e., valid for C99 or C11 or later.
is meany by "C". If you want to pick an older standard, it is best to
specify it explicitly. (And I don't recommend using the term "ANSI C"
at all - people often use it to mean C89, when in fact it means "the
current ISO C standard" - i.e., C18 at the time of writing.)
Of course you are correct that putting declarations in the "for" loop
was introduced in C99. Rounded to the nearest percentage, 100% of C
code has been written since the introduction of C99, and probably at
least 98% since it became widely supported by common tools. There are a
few very niche situations where it makes sense to use pre-C99 today,
other than for maintaining old programs in the style in which they were written. Other than that, C99 syntax is standard.
So, one could
argue, that to ensure backwards code compatibility, hence portability
of C code, that declaring a variable somewhere within a procedure, such
as within a for() loop, should be avoided, yes? Think of C style guide suggestions.
No.
That's like saying software should be published as printouts in a
magazine, rather than, say, on a web page, for backwards compatibility.
On Tuesday, December 7, 2021 at 8:40:03 AM UTC-6, David Brown wrote:
On 07/12/2021 13:52, Rod Pemberton wrote:
As for C, variable declarations within the for() loop is not validC18 is the current standard - without other qualifications, that is what
for ANSI C (C89), i.e., valid for C99 or C11 or later.
is meany by "C". If you want to pick an older standard, it is best to
specify it explicitly. (And I don't recommend using the term "ANSI C"
at all - people often use it to mean C89, when in fact it means "the
current ISO C standard" - i.e., C18 at the time of writing.)
Of course you are correct that putting declarations in the "for" loop
was introduced in C99. Rounded to the nearest percentage, 100% of C
code has been written since the introduction of C99, and probably at
least 98% since it became widely supported by common tools. There are a
few very niche situations where it makes sense to use pre-C99 today,
other than for maintaining old programs in the style in which they were
written. Other than that, C99 syntax is standard.
So, one couldNo.
argue, that to ensure backwards code compatibility, hence portability
of C code, that declaring a variable somewhere within a procedure, such
as within a for() loop, should be avoided, yes? Think of C style guide
suggestions.
That's like saying software should be published as printouts in a
magazine, rather than, say, on a web page, for backwards compatibility.
Also going with 'no'. If you need backwards compatibility, wrap some extra braces in there making a new compound statement extending to the end
of the function. Then you can declare new variables in the middle of a function even in old timey C.
For loops require a slight adjustment. Pull the declaration out front and wrap the whole thing in extra braces.
{
int i;
for( i=0; ...){ ... }
}
Now you're cookin' with gas.
On 10/12/2021 06:00, luserdroog wrote:
For loops require a slight adjustment. Pull the declaration out front and
wrap the whole thing in extra braces.
{
int i;
for( i=0; ...){ ... }
}
Now you're cookin' with gas.
Yes, you certainly /can/ do this with C90. But it quickly becomes quite
ugly if your functions are big. It is better, where possible, to split things into smaller functions.
If you are still coding in C90 today (other than small changes to
maintain legacy code), it is likely to be because you are stuck with an ancient compiler with poor optimisation - it's going to be poor at
inlining so you need to write big functions (and horrendous
function-like macros) if speed is an issue. There isn't really a good solution here if you like clear code - just options that are bad in
different ways.
As far as I am concerned, the habit of putting all variable definitions
at the start of a function, before any statements, is as legacy and
out-dated as non-prototype function declarations or the explicit use of "auto" for local variables.
We don't use Latin to talk about science. We don't program in Algol 68.
Let's leave C90's limitations to the history books too, as much as we reasonably can.
On 10/12/2021 08:17, David Brown wrote:
On 10/12/2021 06:00, luserdroog wrote:
For loops require a slight adjustment. Pull the declaration out front
and
wrap the whole thing in extra braces.
{
int i;
for( i=0; ...){ ... }
}
Now you're cookin' with gas.
Yes, you certainly /can/ do this with C90. But it quickly becomes quite
ugly if your functions are big. It is better, where possible, to split
things into smaller functions.
If you are still coding in C90 today (other than small changes to
maintain legacy code), it is likely to be because you are stuck with an
ancient compiler with poor optimisation - it's going to be poor at
inlining so you need to write big functions (and horrendous
function-like macros) if speed is an issue. There isn't really a good
solution here if you like clear code - just options that are bad in
different ways.
As far as I am concerned, the habit of putting all variable definitions
at the start of a function, before any statements, is as legacy and
out-dated as non-prototype function declarations or the explicit use of
"auto" for local variables.
Yet it makes for tidier looking code. There is a separation between the
logic of the code, and the less important details of the types of
variables, which now no longer clutter up the logic.
To find a variable's type, you just glance up at the 'cast-list' at the
top of the function.
If transfering code between languages, the executable code is likely to
be more portable sans its type declarations, which are going to be language-specific. The other language may not even need types.
You also don't need to worry about block scope: declare that 'int i'
right here, and then you find you can't access 'i' beyond the next '}',
or find (eventually) that it is using a more outer 'i' - the wrong one.
And you don't need to worry about rearranging code where you'd need to
keep ensuring that the first use of a variable is that one with the declaration.
I wonder if, when declaring module-scope variables, macros, enums, types
and so on, whether you place these before all the functions, or
intersperse them between the functions, to have them as close as
possible to where they are first used?
We don't use Latin to talk about science. We don't program in Algol 68.
Let's leave C90's limitations to the history books too, as much as we
reasonably can.
Factual books still tend to have glossaries at one end of the book or
the other.
(BTW Algol68 allowed declarations interspersed with statements before C
did.
I allow the same now (though with function-wide scope to avoid the
problems above), but tend to use that ability as often as I use 'goto'; something that is normally best avoided.)
On 10/12/2021 12:16, Bart wrote:
On 10/12/2021 08:17, David Brown wrote:
On 10/12/2021 06:00, luserdroog wrote:
For loops require a slight adjustment. Pull the declaration out front
and
wrap the whole thing in extra braces.
{
int i;
for( i=0; ...){ ... }
}
Now you're cookin' with gas.
Yes, you certainly /can/ do this with C90. But it quickly becomes quite >>> ugly if your functions are big. It is better, where possible, to split >>> things into smaller functions.
If you are still coding in C90 today (other than small changes to
maintain legacy code), it is likely to be because you are stuck with an
ancient compiler with poor optimisation - it's going to be poor at
inlining so you need to write big functions (and horrendous
function-like macros) if speed is an issue. There isn't really a good
solution here if you like clear code - just options that are bad in
different ways.
As far as I am concerned, the habit of putting all variable definitions
at the start of a function, before any statements, is as legacy and
out-dated as non-prototype function declarations or the explicit use of
"auto" for local variables.
Yet it makes for tidier looking code. There is a separation between the
logic of the code, and the less important details of the types of
variables, which now no longer clutter up the logic.
The type of variables is a critical part of their usage - not some extra feature.
To be fair, there are certainly languages with weak or no typing, which
can be useful for simple tasks. And it's not unreasonable to use
generic integer types that are big enough for most purposes without
being fussy about the type.
But outside of that, types are vital information. A strong typing
system in a language /hugely/ reduces the risks of errors, as well as improving the efficiency of the language, and is particularly important
for larger programs.
So moving types away from the use of a variable is hiding useful
information, not making the code "tidier".
On 10/12/2021 16:51, David Brown wrote:
On 10/12/2021 12:16, Bart wrote:
On 10/12/2021 08:17, David Brown wrote:
On 10/12/2021 06:00, luserdroog wrote:
For loops require a slight adjustment. Pull the declaration out front >>>>> and
wrap the whole thing in extra braces.
{
int i;
for( i=0; ...){ ... }
}
Now you're cookin' with gas.
Yes, you certainly /can/ do this with C90. But it quickly becomes
quite
ugly if your functions are big. It is better, where possible, to split >>>> things into smaller functions.
If you are still coding in C90 today (other than small changes to
maintain legacy code), it is likely to be because you are stuck with an >>>> ancient compiler with poor optimisation - it's going to be poor at
inlining so you need to write big functions (and horrendous
function-like macros) if speed is an issue. There isn't really a good >>>> solution here if you like clear code - just options that are bad in
different ways.
As far as I am concerned, the habit of putting all variable definitions >>>> at the start of a function, before any statements, is as legacy and
out-dated as non-prototype function declarations or the explicit use of >>>> "auto" for local variables.
Yet it makes for tidier looking code. There is a separation between the
logic of the code, and the less important details of the types of
variables, which now no longer clutter up the logic.
The type of variables is a critical part of their usage - not some extra
feature.
To be fair, there are certainly languages with weak or no typing, which
can be useful for simple tasks. And it's not unreasonable to use
generic integer types that are big enough for most purposes without
being fussy about the type.
But outside of that, types are vital information. A strong typing
system in a language /hugely/ reduces the risks of errors, as well as
improving the efficiency of the language, and is particularly important
for larger programs.
So moving types away from the use of a variable is hiding useful
information, not making the code "tidier".
With an algorithm in a dynamic language, you don't need explicit types.
When in a complex macro, or in a template body, then precise types don't matter much either.
Concrete types will be needed when such code is instantiated or
expanded, or when that dynamic code is actually run. But need not be essential for understanding the code.
On 08/12/2021 17:58, Bart wrote:
On 08/12/2021 15:36, David Brown wrote:
On 08/12/2021 12:55, Bart wrote:
What is it with you and your campaign to claim everything C is
bad, and everything in your useless little private language is
good?
I said the rules are complex. You said they are simple. I
disagreed, and illustrated my point with a chart.
A chart designed purely to make the simple rules of C appear complex -
it is FUD.
You added those of your own language, which is utterly
irrelevant to C, purely to be able to claim that the rules of your
language are simple.
Note that even if your language's rules are
simpler in some way, that does /not/ make C's rules complex!
Are you sure you aren't twisting and making up things yourself?
to make
it look more complicated.
I think most people would be surprised at how untidy that chart is.
/I/ was.
You seem to find just about everything in C surprising.
But let's be clear here. Do you think people familiar and experienced
with C programming will find C's rules surprising?
Does it print 1, or 1.5 ?
My languages have two divide operators: "/" and "%".
"%" means integer divide. "/" is supposed to be for floating point
divide, but that's only on one language; the static one will
still do integer divide when both operands are integers.
Genius. Does it also use "and" as a keyword for the remainder
after division? Nothing says "simple" and "intuitive" like
picking different meanings for your operators than all other
languages.
"%" was used for integer divide in Pascal. I adopted it in the 1980s
when I needed distinct operators.
And I use "rem" for integer REMainder instead of "%"; "ixor"
instead of "^"; "ior" instead of "|" and "or" instead of "||".
Maybe it's just me, but I find them more readable.
Why, what do other languages use for integer divide?
Most use /. And in most languages, if they have % operator for
integers, it means modulus. (Conventions differ regarding rounding
and signs when dividing by negative integers.)
On 08/12/2021 15:36, David Brown wrote:
On 08/12/2021 12:55, Bart wrote:
Does it print 1, or 1.5 ?
My languages have two divide operators: "/" and "%".
"%" means integer divide. "/" is supposed to be for floating point
divide, but that's only on one language; the static one will still
do integer divide when both operands are integers.
Genius. Does it also use "and" as a keyword for the remainder after division? Nothing says "simple" and "intuitive" like picking
different meanings for your operators than all other languages.
"%" was used for integer divide in Pascal. I adopted it in the 1980s
when I needed distinct operators.
And I use "rem" for integer REMainder instead of "%"; "ixor" instead
of "^"; "ior" instead of "|" and "or" instead of "||". Maybe it's
just me, but I find them more readable.
On Wed, 8 Dec 2021 16:58:54 +0000
Bart <bc@freeuk.com> wrote:
On 08/12/2021 15:36, David Brown wrote:
Genius. Does it also use "and" as a keyword for the remainder after
division? Nothing says "simple" and "intuitive" like picking
different meanings for your operators than all other languages.
"%" was used for integer divide in Pascal. I adopted it in the 1980s
when I needed distinct operators.
I knew I'd seen that somewhere, but it wasn't Forth or Postscript.
Pascal was 1980s the last time I used it. It was too limited. It
didn't have pointers back then. You were restricted to the Pascal
scope or space, and usually couldn't program the machine it was running
on.
On Wed, 8 Dec 2021 16:36:33 +0100
David Brown <david.brown@hesbynett.no> wrote:
But what I /do/ react against is lies, FUD, and misrepresentations. I
am not "pro-C" - I am "anti-FUD", and it just so happens that your
bizarre hatred of C means it is C you post rubbish about. I'd react
against anyone else deliberately and repeatedly writing nonsense about
other topics too.
Well, that would be you.
Your recent incorrect post to comp.compilers about type-punning and
your insistence that arrays in C aren't actually just pointers, i.e., pass-by-reference, but instead magically "decay into" pointers.
You need to learn about how the subscript operator [] in C actually
works. It takes a pointer and an index in either order. It
doesn't take arrays. Then, read up on Dennis Ritchie's papers where he states that make the language syntax appear to match variable
declarations, because not doing so confusing to newbs. You should then realize that C only has array declarations to allocate storage and
check type, and no actual arrays in C, as they are pointers, and array
syntax is simulated by the subscript operator.
On 01/12/2021 08:43, David Brown wrote:
[I wrote:]
Zero, as a number, was invented(Historical note:
in modern times [FSVO "modern"!].
It reached Europe around 1200, but had been around in India, amongst
other countries, for a good while before that.
Yes, but that's nearly always zero as a placeholder, not
as a number in its own right. [I'm not convinced by many of the
claimed exceptions, which often smack of flag-waving.]
Whatever kind of numbers you use, you have to apply a few brain cells.
You can't represent 1/3 with an integer, no matter how big it is. You
can't represent negative numbers with unsigned types. It's common
sense, not a "problematic limit".
Anyone who finds it surprising that
you can't subtract 3 from 2 without signed numbers should give up their programming career and go back to primary school. We have to have
/some/ standard of education in this profession!
David Brown <david.brown@hesbynett.no> wrote:
Whatever kind of numbers you use, you have to apply a few brain cells.
You can't represent 1/3 with an integer, no matter how big it is. You
can't represent negative numbers with unsigned types. It's common
sense, not a "problematic limit".
But common sense is wrong here.
On 23/01/2022 15:34, antispam@math.uni.wroc.pl wrote:
David Brown <david.brown@hesbynett.no> wrote:
Whatever kind of numbers you use, you have to apply a few brain cells.
You can't represent 1/3 with an integer, no matter how big it is. You
can't represent negative numbers with unsigned types. It's common
sense, not a "problematic limit".
I'm trying to figure out what you are talking about here when you are resurrecting a long-finished thread.
But common sense is wrong here.
No, it is not.
Introducing new operations, or new ways to interpret numbers, does not
help. Being able to do something in /theory/ does not necessarily help
in /practice/.
The rationals are countably infinite. It is therefore possible to
represent any rational number (including negative ones) using a
non-negative integer, by producing an appropriate coding scheme. That
does not help in any real use, however. Similarly, you could call a 1
MB executable an 8-million bit integer, but to what purpose? It's
wonderful for proving results about computable problems, but not for practical programming.
Invertible numbers in modulo arithmetic are equally useless for normal arithmetic. (They certainly have their uses in encryption and other
fields.) Who cares if 171 is the inverse of 3 modulo 256? What does it
give you? Calculating that 171 is a time-consuming process.
You can't
use it to divide by 3 - you only get a useful answer if the numerator
happens to be divisible by 3.
You can't find the inverse of 6.
You
can't distinguish it from 171,
despite 171 being an entirely different
number from 1/3. It does not give you a useful way to represent fractions.
If I remember this thread correctly, the point was that any finite and limited representation is going to have limits on what it can represent.
David Brown <david.brown@hesbynett.no> wrote:
On 23/01/2022 15:34, antispam@math.uni.wroc.pl wrote:
David Brown <david.brown@hesbynett.no> wrote:
Whatever kind of numbers you use, you have to apply a few brain cells. >>>> You can't represent 1/3 with an integer, no matter how big it is. You >>>> can't represent negative numbers with unsigned types. It's common
sense, not a "problematic limit".
I'm trying to figure out what you are talking about here when you are
resurrecting a long-finished thread.
But common sense is wrong here.
No, it is not.
Introducing new operations, or new ways to interpret numbers, does not
help. Being able to do something in /theory/ does not necessarily help
in /practice/.
The rationals are countably infinite. It is therefore possible to
represent any rational number (including negative ones) using a
non-negative integer, by producing an appropriate coding scheme. That
does not help in any real use, however. Similarly, you could call a 1
MB executable an 8-million bit integer, but to what purpose? It's
wonderful for proving results about computable problems, but not for
practical programming.
Invertible numbers in modulo arithmetic are equally useless for normal
arithmetic. (They certainly have their uses in encryption and other
fields.) Who cares if 171 is the inverse of 3 modulo 256? What does it
give you? Calculating that 171 is a time-consuming process.
Sure, like any computation it needs time.
You can't
use it to divide by 3 - you only get a useful answer if the numerator
happens to be divisible by 3.
Not only. If you have apropriate bounds, say numerator has absolute value less than 10 and denominator is between 1 and 12 you can _uniqely_ reconstruct fraction from such representation. Of course, range of
fractions representable in single byte is small, if you need more
use bigger integer type.
You can't find the inverse of 6.
Yes, here one needs different modulus, so no longer can directly
use machine arithmetic.
You
can't distinguish it from 171,
Well, you need enough bits to have unique representation, there is
no way around this.
despite 171 being an entirely different
number from 1/3. It does not give you a useful way to represent fractions.
It is useful as in "used by actual programs" which produce fractions
as results.
If I remember this thread correctly, the point was that any finite and
limited representation is going to have limits on what it can represent.
Sure. However, IMO your wording was unfortunate. You probably do
not need such representation and normal programming languages have
no support for it. But this is tradof based on popular needs
and not an absolute impossibility.
On 24/01/2022 17:08, antispam@math.uni.wroc.pl wrote:
David Brown <david.brown@hesbynett.no> wrote:
On 23/01/2022 15:34, antispam@math.uni.wroc.pl wrote:
David Brown <david.brown@hesbynett.no> wrote:
Whatever kind of numbers you use, you have to apply a few brain cells. >>>> You can't represent 1/3 with an integer, no matter how big it is. You >>>> can't represent negative numbers with unsigned types. It's common
sense, not a "problematic limit".
I'm trying to figure out what you are talking about here when you are
resurrecting a long-finished thread.
But common sense is wrong here.
No, it is not.
Introducing new operations, or new ways to interpret numbers, does not
help. Being able to do something in /theory/ does not necessarily help
in /practice/.
The rationals are countably infinite. It is therefore possible to
represent any rational number (including negative ones) using a
non-negative integer, by producing an appropriate coding scheme. That
does not help in any real use, however. Similarly, you could call a 1
MB executable an 8-million bit integer, but to what purpose? It's
wonderful for proving results about computable problems, but not for
practical programming.
Invertible numbers in modulo arithmetic are equally useless for normal
arithmetic. (They certainly have their uses in encryption and other
fields.) Who cares if 171 is the inverse of 3 modulo 256? What does it >> give you? Calculating that 171 is a time-consuming process.
Sure, like any computation it needs time.
You can't
use it to divide by 3 - you only get a useful answer if the numerator
happens to be divisible by 3.
Not only. If you have apropriate bounds, say numerator has absolute value less than 10 and denominator is between 1 and 12 you can _uniqely_ reconstruct fraction from such representation. Of course, range of fractions representable in single byte is small, if you need more
use bigger integer type.
You can't find the inverse of 6.
Yes, here one needs different modulus, so no longer can directly
use machine arithmetic.
You
can't distinguish it from 171,
Well, you need enough bits to have unique representation, there is
no way around this.
Yes, and that is a key point.
Another important point is that if you want to represent rationals in a practical and useful manner, this is not the way to do it - a pair of integers (of some fixed size, or of variable size) is vastly more
convenient for most purposes.
despite 171 being an entirely different
number from 1/3. It does not give you a useful way to represent fractions.
It is useful as in "used by actual programs" which produce fractions
as results.
No, it will not be - the format is too inconvenient for most purposes.
There may be niche cases where it is useful (I am guessing that
cryptography could be one area, but I am not an expert there).
The same applies to other types of "number", such as doing your
arithmetic over the Galois Field GF(2?). Now every number from 1 to 255
has a unique multiplicative inverse. This can let you do marvellous
things - such as guaranteeing a solution to the simultaneous equations
used to restore RAID6 disk sets when two drives are dead. But it also
means that while 20 / 5 is 4, 20 / 3 is 12 and 20 / 6 is 6.
It is
useless for "normal" division (or normal multiplication, addition or subtraction).
David Brown <david.brown@hesbynett.no> wrote:
On 24/01/2022 17:08, antispam@math.uni.wroc.pl wrote:
David Brown <david.brown@hesbynett.no> wrote:
On 23/01/2022 15:34, antispam@math.uni.wroc.pl wrote:
David Brown <david.brown@hesbynett.no> wrote:
Whatever kind of numbers you use, you have to apply a few brain cells. >>>>>> You can't represent 1/3 with an integer, no matter how big it is. You >>>>>> can't represent negative numbers with unsigned types. It's common >>>>>> sense, not a "problematic limit".
I'm trying to figure out what you are talking about here when you are
resurrecting a long-finished thread.
But common sense is wrong here.
No, it is not.
Introducing new operations, or new ways to interpret numbers, does not >>>> help. Being able to do something in /theory/ does not necessarily help >>>> in /practice/.
The rationals are countably infinite. It is therefore possible to
represent any rational number (including negative ones) using a
non-negative integer, by producing an appropriate coding scheme. That >>>> does not help in any real use, however. Similarly, you could call a 1 >>>> MB executable an 8-million bit integer, but to what purpose? It's
wonderful for proving results about computable problems, but not for
practical programming.
Invertible numbers in modulo arithmetic are equally useless for normal >>>> arithmetic. (They certainly have their uses in encryption and other
fields.) Who cares if 171 is the inverse of 3 modulo 256? What does it >>>> give you? Calculating that 171 is a time-consuming process.
Sure, like any computation it needs time.
You can't
use it to divide by 3 - you only get a useful answer if the numerator
happens to be divisible by 3.
Not only. If you have apropriate bounds, say numerator has absolute value >>> less than 10 and denominator is between 1 and 12 you can _uniqely_
reconstruct fraction from such representation. Of course, range of
fractions representable in single byte is small, if you need more
use bigger integer type.
You can't find the inverse of 6.
Yes, here one needs different modulus, so no longer can directly
use machine arithmetic.
You
can't distinguish it from 171,
Well, you need enough bits to have unique representation, there is
no way around this.
Yes, and that is a key point.
Another important point is that if you want to represent rationals in a
practical and useful manner, this is not the way to do it - a pair of
integers (of some fixed size, or of variable size) is vastly more
convenient for most purposes.
Rationals as pairs of integers of fixed size are a toy, useless
if you want to do serious computations. Basic point about rationals
is that there is tendency to have very big numerators and denominators. Without simplifying common factors in many cases one gets exponential
growth of length of numerators and denominators. Canceling common
factors is expensive and still there is growth (but only linear).
despite 171 being an entirely different
number from 1/3. It does not give you a useful way to represent fractions.
It is useful as in "used by actual programs" which produce fractions
as results.
No, it will not be - the format is too inconvenient for most purposes.
There may be niche cases where it is useful (I am guessing that
cryptography could be one area, but I am not an expert there).
I wrote my sentence in present tense, future is too hard to predict...
You may consider exact computations as a niche. But by this token
probably most programs are niche: there is small number of widely
used popular programs and long tail of specialized programs that each
have relativly small number of users. It is pretty clear that
when you count programs (as opposed to users) most programs will
be in this long tail.
BTW: I am not aware of serious use of Z(2^n) in crypthography
(I am not saying it is not used, just that I mainstream
cryptosystem that heard about do not use it)
From my point of view crypthography is good because thanks to
crypthography processor makers got more serious about performance
of multiprecision arithmetic. But I am really not going deep
into crypthography...
The same applies to other types of "number", such as doing your
arithmetic over the Galois Field GF(2?). Now every number from 1 to 255
has a unique multiplicative inverse. This can let you do marvellous
things - such as guaranteeing a solution to the simultaneous equations
used to restore RAID6 disk sets when two drives are dead. But it also
means that while 20 / 5 is 4, 20 / 3 is 12 and 20 / 6 is 6.
Not sure what you mean here: GF(2^n) is different than Z(2^n) and
there is no natural correspondence between elements of GF(2^n)
and Z(2^n).
In particular in GF(2^n) we have 1 + 1 = 0, so natural
image of 2 = 1 + 1 definitely is not invertible.
If you fix irreducible
polynomial there is correspondence between bitstrings of length n
and elements of GF(2^n). But to multiply bitstrings you should
do "carryless mutiplication" and then reduce modulo irreducible
polynomial...
It is
useless for "normal" division (or normal multiplication, addition or
subtraction).
GF(2^n) has uses when you deal with polynomials. But unless
hardware efficiently supports carryless mutiplication it is
usually more efficient to use Z(p) for prime p > 2. That
is assuming that you care only about polynomials with rational
cofficients (if you deal with Z(2) you may be forced to use GF(2^n)).
On 02/12/2021 21:25, James Harris wrote:
On 02/12/2021 20:11, Bart wrote:
Continuous measurements need to start from 0.0.
Discrete entities are counted, starting at 0 for none, then 1 for 1 (see
Xs below).
Some are in-between, where continuous quantities are represented as lots
of small steps. (Example: money in steps of £0.01, or time measured in
whole seconds.)
But do you see the point of it? The first century /naturally/ had
century number zero, not one, and the N'th century has century number
N - 1
IOW the numbering begins at zero.
Define what you mean by numbering first.
For me it means assigning sequential integers to a series of entities.
But you need an entity to hang a number from. With no entities, where
are you going to stick that zero?
I'm not sure what you're trying to argue here; that because 0 is used to
mean nothing, then that must be the start point for everything?
Here are some sets of Xs increasing in size:
How many X's? Numbered as? Number of the Last?
--------
- 0 - -
--------
X 1 1 1
--------
X X 2 1 2 2
--------
X X X 3 1 2 3 3
--------
How would /you/ fill in those columns? I'd guess my '1 2 3' becomes '0 1
2', and that that last '3' becomes '2'.
But what about the first '3' on that last line; don't tell me it becomes
'2'! (Because then what happens to the '0'?)
Using you scheme (as I assume it will be); there is too much disconnect:
a '0' in the first row, and two 0s the second; a '1' in the second, and
two 1s in the third. Everything is out of step!
Yes, you are talking about discreet units which are not made of parts.
Yes, arrays of elements that are computer data with no physical dimensions.
On 02/12/2021 22:25, James Harris wrote:
That's not a convention, by the way, but how all numbering works: thingsNote, however, that the first century began with year 1 AD (or 1 CE, if
with partial phases begin at zero.
you prefer). The preceding year was 1 BC. There was no year 0. This
means the first century was the years 1 to 100 inclusive.
It really annoyed me that everyone wanted to celebrate the new
millennium on 01.01.2000, when in fact it did not begin until 01.01.2001.
It would have been so much simpler, and fitted people's expectations
better, if years have been numbered from 0 onwards instead of starting counting at 1.
On 03/12/2021 09:08, David Brown wrote:
On 02/12/2021 22:42, James Harris wrote:
On 02/12/2021 20:49, Dmitry A. Kazakov wrote:
On 2021-12-02 21:31, James Harris wrote:
...
But to the point, are you comfortable with the idea of the A(2) in
x = A(2) + 0
meaning the same mapping result as the A(2) in
A(2) = 0
?
Yes, in both cases the result is the array element corresponding to
the index 2. That is the semantics of A(2).
Cool. If A were, instead, a function that, say, ended with
return v
then what would you want those A(2)s to mean and should they still mean
the same as each other? The latter expression would look strange to
many.
Do you mean like returning a reference in C++ style?
Hi David, apologies for not replying before. I was just now looking for
old posts that were outstanding in some way. (There may be many which I
have yet to reply to like your one.)
I probably left your post until I found out about C++ references and
never got round to reading up on them.
int a[10];
void foo1(int i, int x) {
a[i] = x;
}
int& A(int i) {
return a[i];
}
void foo2(int i, int x) {
A(i) = x;
}
foo1 and foo2 do the same thing, and have the same code. Of course,
foo2 could add range checking, or offsets (for 1-based array), or have
multiple parameters for multi-dimensional arrays, etc. And in practice
you'd make such functions methods of a class so that the class owns the
data, rather than having a single global source of the data.
I see at https://www.geeksforgeeks.org/references-in-cpp/ code which
includes
void swap(int& first, int& second)
{
int temp = first;
first = second;
second = temp;
}
That's not what I was thinking about. I don't care for it because in a
call such as
swap(a, b)
it's not clear in the syntax that the arguments a and b can be modified
- a calamity of a design, IMO. :-(
But in your case you are referring to a /returned/ value. That appears
to be OK except that to match what I had in mind I think there should be
a const in there somewhere. To illustrate, I was proposing that in a function, f, one could have
return h
which would /conceptually/ return the address of h (which I guess in
Algol terms means it would return h rather than the value of h).
Crucially, and perhaps at variance with Algol (I don't know) the value
of h (i.e. the value at the returned address) would be read-only to the caller.
The caller would be able to use the address returned as it could any
other address, but it could not write over the referenced value. If the callee returned with something like
return a[4]
then it would conceptually return the address of a[4] and, again, the
value at the returned address would be read-only in the caller.
What I've said so far is by default but overwriting would be possible.
To conceptually return the address of a variable which /could/ be
overwritten one would use the rw modifier as in
return rw h
or
return rw a[4]
I'll say no more just now as this is an old topic but I wanted to at
least make a reply.
[...] To illustrate, I was
proposing that in a function, f, one could have
return h
which would /conceptually/ return the address of h (which I guess in
Algol terms means it would return h rather than the value of h).
Crucially, and perhaps at variance with Algol (I don't know) the
value of h (i.e. the value at the returned address) would be
read-only to the caller.
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 293 |
Nodes: | 16 (2 / 14) |
Uptime: | 218:12:38 |
Calls: | 6,621 |
Calls today: | 3 |
Files: | 12,171 |
Messages: | 5,317,781 |