Discussion:
BASIC oddity?
(too old to reply)
Alan Adams
2020-01-03 17:16:18 UTC
Permalink
I've either discovered something odd but useful about BASIC, or I#ve got
myself rather confused.

The issue arose because I need to subtract one 5-byte time from another,
returning the result in an integer.

This will obviously fail if the result is too large to fit in an integer,
but that is a large number of days, and will not occur in my particular
case, as the computers will not need to run that long without a restart.

What I thinki I've discovered is that although BASIC treats values that go
beyone &7fffffff as negative numbers, when suntracting them one from
another it treats them as unsigned. The following example demonstrates why
I think this is the case.

Note that I'm ignoring the MSB of the 5-byte times, as the test was
created to fnd out what I needed to do with it, and seems to show that I
simply ignore it.

The weird example is the third, where subtracting a very large positive
number from a very large negative number produces a positive result.

Comments please:

REM > TimingTest

DEBUG%=1

DIM currentbuf% 5, baseline% 5

!baseline%=&80
?(baseline%+4)=&58
!currentbuf%=&184
?(currentbuf%+4)=&58
PRINT FNsubtimes(currentbuf%,baseline%)

!baseline%=&7FFFFE00
?(baseline%+4)=&58
!currentbuf%=&7FFFFF04
?(currentbuf%+4)=&58
PRINT FNsubtimes(currentbuf%,baseline%)

!baseline%=&7FFFFF80
?(baseline%+4)=&58
!currentbuf%=&80000084
?(currentbuf%+4)=&50
PRINT FNsubtimes(currentbuf%,baseline%)

!baseline%=&FFFFFF80
?(baseline%+4)=&58
!currentbuf%=&84
?(currentbuf%+4)=&58
PRINT FNsubtimes(currentbuf%,baseline%)

END

DEF FNsubtimes(buf1%,buf2%)
REM subtract 5-byte time in buf2% from buf1%
LOCAL I%,J%,K%
PRINT
I%=!buf1%
J%=!buf2%
K%=I%-J%
REM IF DEBUG%AND1 THEN *REPORT TL: subtimes: I% J% ~I% ~J% K%
PRINT I% J% K% ~I% ~J% ~K%
=K%
ENDPROC
--
Alan Adams, from Northamptonshire
***@adamshome.org.uk
http://www.nckc.org.uk/
Martin
2020-01-03 19:31:03 UTC
Permalink
Post by Alan Adams
I've either discovered something odd but useful about BASIC, or
I#ve got myself rather confused.
The issue arose because I need to subtract one 5-byte time from
another, returning the result in an integer.
This will obviously fail if the result is too large to fit in an
integer, but that is a large number of days, and will not occur in
my particular case, as the computers will not need to run that
long without a restart.
What I thinki I've discovered is that although BASIC treats values
that go beyone &7fffffff as negative numbers, when suntracting
them one from another it treats them as unsigned. The following
example demonstrates why I think this is the case.
Note that I'm ignoring the MSB of the 5-byte times, as the test was
created to fnd out what I needed to do with it, and seems to show
that I simply ignore it.
The weird example is the third, where subtracting a very large
positive number from a very large negative number produces a
positive result.
I think you are confused. BASIC integers are *always* 4 bytes, never
5 bytes. The statement
I%=!buf1%
sets I% to the first 4 bytes at address buf1%

Which I think will explain your results.

There is no way for BASIC to subtract two 5-byte integers, apart from
spliiting each to one 5-byte integer and one 1-bute - and handling
the case where the result is negative.

However, if the difference is small, or you can ignore the lest
significant byte, you may be able to chose a single 4-bytes that will
provide the difference.
--
Martin Avison
Note that unfortunately this email address will become invalid
without notice if (when) any spam is received.
druck
2020-01-03 21:00:23 UTC
Permalink
Post by Martin
There is no way for BASIC to subtract two 5-byte integers, apart from
spliiting each to one 5-byte integer and one 1-bute - and handling
the case where the result is negative.
I'd always opt for a bit of assembler to do that, as its every easy to
do arbitrary sized arithmetic when you've got access to the CPU flags.
Post by Martin
However, if the difference is small, or you can ignore the lest
significant byte, you may be able to chose a single 4-bytes that will
provide the difference.
I'd never recommend that, no matter how sort the interval, an over flow
will occur at some point (the next in about 10 years from now).

But anyway this highlights the lack of date and time manipulation SWIs
in RISC OS. The only things you can do with a 5 byte time is convert it
to or from a string.

---druck
Alan Adams
2020-01-03 22:26:15 UTC
Permalink
Post by druck
Post by Martin
There is no way for BASIC to subtract two 5-byte integers, apart from
spliiting each to one 5-byte integer and one 1-bute - and handling
the case where the result is negative.
I'd always opt for a bit of assembler to do that, as its every easy to
do arbitrary sized arithmetic when you've got access to the CPU flags.
Post by Martin
However, if the difference is small, or you can ignore the lest
significant byte, you may be able to chose a single 4-bytes that will
provide the difference.
I'd never recommend that, no matter how sort the interval, an over flow
will occur at some point (the next in about 10 years from now).
But anyway this highlights the lack of date and time manipulation SWIs
in RISC OS. The only things you can do with a 5 byte time is convert it
to or from a string.
---druck
I was starting to think of using assembler.

The difference between the times will never be more than a day. However
the times are the time stored in the computer, which is close to the
actual date and time. So it's not impossible that at some point each of
the edge cases in the example might occur, i/e.

low word goes from 7f ff ff ff to 80 00 00 00
Low word goes from ff ff ff ff to 00 00 00 00 and the high byte is
incremented.
(I'm going to ignore the case when time goes from ff ff ff ff ff to 00 00
00 00 00.)

The example was written so that I could get my head round what happened
when I subtracted 7f ff ff f0 from 80 00 00 0f for example. What I really
didn't expect was a positive, and correct, answer, since PRINTing the
variables said I was subtracting a large positive number from a large
negative one. I expected a negative underflow, not a valid positive
result.

(My suspicion is that the calculation is done ignoring the underflow, and
the result returned is the remainder.)

If I could assume that this behaviour of BASIC would continue unchanged,
then I could make use of it. Since it seems to me to be incorrect
(although useful) I really ought to avoid using it, in case it gets
corrected, and the only way I know to do that is assembler.
--
Alan Adams, from Northamptonshire
***@adamshome.org.uk
http://www.nckc.org.uk/
Steve Drain
2020-01-04 10:40:38 UTC
Permalink
Post by druck
I'd always opt for a bit of assembler to do that, as its every easy
to do arbitrary sized arithmetic when you've got access to the CPU
flags.
+1
Post by druck
But anyway this highlights the lack of date and time manipulation
SWIs in RISC OS. The only things you can do with a 5 byte time is
convert it to or from a string.
I approached this lack ages ago in Basalt, which has a comprehensive set
of datetime keywords. 5-byte datetime values are held in float
variables, but the float value is meaningless except when used with
appropriate keywords.

So, you could do:

interval%=SECONDS(datetime2 - datetime1)

;-)

Maybe I could develop a module from this.
Martin
2020-01-04 16:08:00 UTC
Permalink
Post by Steve Drain
Post by druck
I'd always opt for a bit of assembler to do that, as its every
easy to do arbitrary sized arithmetic when you've got access to
the CPU flags.
+1
Post by druck
But anyway this highlights the lack of date and time manipulation
SWIs in RISC OS. The only things you can do with a 5 byte time is
convert it to or from a string.
I approached this lack ages ago in Basalt, which has a
comprehensive set of datetime keywords. 5-byte datetime values are
held in float variables, but the float value is meaningless except
when used with appropriate keywords.
interval%=SECONDS(datetime2 - datetime1)
;-)
Maybe I could develop a module from this.
I have thought about this again, and re-read Alan's original post
more carefully. I would agree with druck's suggestion (although I did
not see his full post) that using assembler to subtract the full 5
bytes correctly is undoubtedly the best way to ensure that all
possible values are catered for correctly.

However, two points should be considered from Alan's post.

1. The answer is a relatively small positive time difference.
If 1 day maximum, this is only 8,640,000 or &83D600.
The MSB 5th byte only changes about every 500 days.

2. Alan said
Post by Steve Drain
The weird example is the third, where subtracting a very large
positive number from a very large negative number produces a
positive result.
But this result is not weird, it is correct - because as they are
both UNSIGNED it is subtracting a very large number from an even
larger number. The result is a small number, which is correct.

The strange thing about 2's complement arithmetic is that it works
for both signed and unsigned numbers. The first 4 bytes of a 5-byte
time block are unsigned, so cannot be regarded as negative. It even
works when the MSB 5th byte has changed, as the last example shows.

So, I would suggest that using
I% = !buf1%
J% = !buf2%
K% = I%-J%
will suffice to calculate a time difference of a few days in cs.

... unless anyone can see the flaws in my argument!
--
Martin Avison
Note that unfortunately this email address will become invalid
without notice if (when) any spam is received.
Alan Adams
2020-01-04 16:22:20 UTC
Permalink
<snip>
Post by Martin
2. Alan said
Post by Alan Adams
The weird example is the third, where subtracting a very large
positive number from a very large negative number produces a
positive result.
But this result is not weird, it is correct - because as they are
both UNSIGNED it is subtracting a very large number from an even
larger number. The result is a small number, which is correct.
The strange thing about 2's complement arithmetic is that it works
for both signed and unsigned numbers. The first 4 bytes of a 5-byte
time block are unsigned, so cannot be regarded as negative. It even
works when the MSB 5th byte has changed, as the last example shows.
So, I would suggest that using
I% = !buf1%
J% = !buf2%
K% = I%-J%
will suffice to calculate a time difference of a few days in cs.
That agrees with what I found. It even seems to work in a case where the
5th (highest) byte is incremented.

The thing I found surprising is that BASIC displays the values in I% and
J% using a signed interpretation, but does the subtraction in an unsigned
fashion. It's this inconsistency that worries me, in case at some future
date is is "corrected".
Post by Martin
... unless anyone can see the flaws in my argument!
--
Alan Adams, from Northamptonshire
***@adamshome.org.uk
http://www.nckc.org.uk/
David Higton
2020-01-04 17:24:18 UTC
Permalink
Post by Alan Adams
The thing I found surprising is that BASIC displays the values in I% and
J% using a signed interpretation, but does the subtraction in an unsigned
fashion. It's this inconsistency that worries me, in case at some future
date is is "corrected".
It's not inconsistent. This is just how 2's complement addition and
subtraction work. Perhaps you need to work through some more examples.
Doing them on paper, perhaps with a more limited number of bits to make
it less laborious, might help convince you.

David
Alan Adams
2020-01-04 18:47:44 UTC
Permalink
Post by David Higton
Post by Alan Adams
The thing I found surprising is that BASIC displays the values in I% and
J% using a signed interpretation, but does the subtraction in an unsigned
fashion. It's this inconsistency that worries me, in case at some future
date is is "corrected".
It's not inconsistent. This is just how 2's complement addition and
subtraction work. Perhaps you need to work through some more examples.
Doing them on paper, perhaps with a more limited number of bits to make
it less laborious, might help convince you.
Ity may not be inconsistent if you view it from the viewpoint of
assembler, but it is very much inconsistent from the viewpoint of BASIC

-(very large number) - (another very large number) should never result in
a positive answer.

BASIC shows these values as negative, and returns positive.

That's inconsistent "within the BASIC language scope". It should, to be
consistent, return an underflow error.

Where it is consistent is that it does the same thing with addition, but
not with multiplication.
Post by David Higton
a%=&7ffffff0
b%=&7fffff00
PRINT a%
2.14748363E9
Post by David Higton
PRINT b%
2.14748339E9
Post by David Higton
PRINT a%+b%
-272
Post by David Higton
PRINT a%*2
4.29496726E9
Post by David Higton
PRINT b%*2
4.29496678E9
Post by David Higton
PRINT ~(a%*2)
Number too big
--
Alan Adams, from Northamptonshire
***@adamshome.org.uk
http://www.nckc.org.uk/
Matthew Phillips
2020-01-06 08:58:48 UTC
Permalink
Post by Alan Adams
Post by David Higton
Post by Alan Adams
The thing I found surprising is that BASIC displays the values in I% and
J% using a signed interpretation, but does the subtraction in an unsigned
fashion. It's this inconsistency that worries me, in case at some future
date is is "corrected".
It's not inconsistent. This is just how 2's complement addition and
subtraction work. Perhaps you need to work through some more examples.
Doing them on paper, perhaps with a more limited number of bits to make
it less laborious, might help convince you.
Ity may not be inconsistent if you view it from the viewpoint of
assembler, but it is very much inconsistent from the viewpoint of BASIC
-(very large number) - (another very large number) should never result in
a positive answer.
BASIC shows these values as negative, and returns positive.
That's inconsistent "within the BASIC language scope". It should, to be
consistent, return an underflow error.
I agree with you, Alan. BBC BASIC is behaving more like a low level language
than I would have expected. It happens to be useful for the some purposes,
but not as useful as it could be - as druck says it's easier to do 5-byte
arithmetic in assembly language because you have access to the flags.

Some implementations of BASIC behave differently. Locomotive BASIC on the
Amstrad CPC has integer types limited to 16 bits, as it happens. This is how
it behaves:

a%=-32768

PRINT a%-&7000
-61440

b%=a%-&7000
Overflow

So you see that the arithmetic is being done correctly, possibly by
converting to floating point which might have more bits of precision
available, but if you try to assign the result to an integer variable, it
knows that it's not going to fit.

Your example code was performing the subtraction, placing the result in K%
and then reporting the value of K%. So what your test does not make clear is
whether BASIC has worked out the correct value of the result, and then
truncated it to fit into the 32-bit variable, or whether it has already
Post by Alan Adams
a%=-127*256*256*256
P.a%
-2.13070643E9
Post by Alan Adams
P.~a%
81000000
Post by Alan Adams
P.a%-&7FFFFFF
2.03004314E9
Post by Alan Adams
P.~(a%-&7FFFFFF)
79000001

So BASIC has produced a large positive result. It appears that it has spotted
that a% and &7FFFFFF can both be represented as signed 32-bit integers, and
has performed the calculation using 32 bits for the result as well.

By contrast if we use a floating point variable...
Post by Alan Adams
a=-127*256*256*256
P.a-&7FFFFFF
-2.26492416E9

More modern scripting languages like Perl will dynamically change the types
of variables to fit what is being put into them, rather than sticking with
the most compact type and ending up with what appear to be wrong answers.
Post by Alan Adams
Where it is consistent is that it does the same thing with addition, but
not with multiplication.
Post by David Higton
a%=&7ffffff0
b%=&7fffff00
PRINT a%
2.14748363E9
Post by David Higton
PRINT b%
2.14748339E9
Post by David Higton
PRINT a%+b%
-272
Post by David Higton
PRINT a%*2
4.29496726E9
Post by David Higton
PRINT b%*2
4.29496678E9
Post by David Higton
PRINT ~(a%*2)
Number too big
I suppose the problem is much more conspicuous with multiplication, and so
the designers of BBC BASIC made more effort to do the right thing.

I'd be interested to see how BBC BASIC behaves on an 8-bit machine like the
BBC B.
--
Matthew Phillips
Durham
David Higton
2020-01-06 16:20:37 UTC
Permalink
Post by David Higton
Post by Alan Adams
The thing I found surprising is that BASIC displays the values in I% and
J% using a signed interpretation, but does the subtraction in an unsigned
fashion. It's this inconsistency that worries me, in case at some future
date is is "corrected".
It's not inconsistent. This is just how 2's complement addition and
subtraction work. Perhaps you need to work through some more examples.
Doing them on paper, perhaps with a more limited number of bits to make
it less laborious, might help convince you.
It may not be inconsistent if you view it from the viewpoint of
assembler, but it is very much inconsistent from the viewpoint of BASIC
-(very large number) - (another very large number) should never result in
a positive answer.
BASIC shows these values as negative, and returns positive.
That's inconsistent "within the BASIC language scope". It should, to be
consistent, return an underflow error.
You're making an assumption that, in BASIC, integers can only be signed,
and that's what everyone wants. Now I know that BASIC represents them
in PRINT statements as signed, but I don't regard that as the end of the
story. I am well used to thinking about whether I want each one of my
integers to be signed or unsigned. I'm quite happy to use BASIC to
handle unsigned integers. The important thing is that it doesn't fall
over with an error; I'm quite happy to interpret the result of the
calculation myself. (I'm also happy to add a signed integer to an
unsigned integer, etc.)

To my mind and my needs, BASIC does the right thing.
Where it is consistent is that it does the same thing with addition, but
not with multiplication.
That's the same in machine language, AFAIUI. Addition and subtraction
instructions don't need to have signed and unsigned variants, but
multiplication (despite being repeated addition) /does/ require them.
I don't understand why, bearing in mind my comment in brackets, and if
anyone can point me to an explanation, I'd be grateful.

David
Matthew Phillips
2020-01-07 08:49:55 UTC
Permalink
Post by David Higton
Post by David Higton
Post by Alan Adams
The thing I found surprising is that BASIC displays the values in I% and
J% using a signed interpretation, but does the subtraction in an unsigned
fashion. It's this inconsistency that worries me, in case at some future
date is is "corrected".
It's not inconsistent. This is just how 2's complement addition and
subtraction work. Perhaps you need to work through some more examples.
Doing them on paper, perhaps with a more limited number of bits to make
it less laborious, might help convince you.
It may not be inconsistent if you view it from the viewpoint of
assembler, but it is very much inconsistent from the viewpoint of BASIC
-(very large number) - (another very large number) should never result in
a positive answer.
BASIC shows these values as negative, and returns positive.
That's inconsistent "within the BASIC language scope". It should, to be
consistent, return an underflow error.
You're making an assumption that, in BASIC, integers can only be signed,
and that's what everyone wants. Now I know that BASIC represents them
in PRINT statements as signed, but I don't regard that as the end of the
story. I am well used to thinking about whether I want each one of my
integers to be signed or unsigned. I'm quite happy to use BASIC to
handle unsigned integers. The important thing is that it doesn't fall
over with an error; I'm quite happy to interpret the result of the
calculation myself. (I'm also happy to add a signed integer to an
unsigned integer, etc.)
To my mind and my needs, BASIC does the right thing.
Where it is consistent is that it does the same thing with addition, but
not with multiplication.
That's the same in machine language, AFAIUI. Addition and subtraction
instructions don't need to have signed and unsigned variants, but
multiplication (despite being repeated addition) /does/ require them.
I don't understand why, bearing in mind my comment in brackets, and if
anyone can point me to an explanation, I'd be grateful.
You'll note that in ARM assembly language MUL does not have signed/unsigned
variants. It takes two 32-bit registers, multiplies them, and puts the result
in a 32-bit register, discarding any overflow. That works just as well for
signed and unsigned numbers.

The reason the long multiplication instructions, SMULL and UMULL, have signed
and unsigned versions is that the result has 64 bits despite the operands
each being 32 bits.

In two's complement representation, if you have a negative number like -1
(11111111,11111111,11111111,11111111) then if you want to put the result in
64-bit form you need to extend it to fill the top 32 bits with 1 as well.

Taking things down to a more manageable size, 8 bits and 16 bits:

In 8 bits, 11111111 is -1 (or 255 if unsigned)

11111111 * 2 is 11111110

That's an overflow if the number was unsigned, but it's -2 if signed.

But if you put the result in a 16 bit register, you will get

00000001 11111110

which is 510, and not -2, unless you have told the processor that you are
doing signed arithmetic, in which case it can make sure that the extra bits
are filled with the appropriate number depending on the sign of the result.

You said about that you're quite happy to interpret the result of the
calculation yourself and that the important thing is that it doesn't fall
over with an error. That's all very well, but there are many applications
where it is better to know about overflows than to ignore them. If you wrote
an accounts package, you might decide to store all the values as integers, in
pennies, to avoid floating point rounding errors. That's fine if you don't
need to deal with any values bigger than £21,474,836.47. But if a business
with a big turnover buys your accounting package, they will get very cross if
you have not guarded against overflow.

It would be an utter pain for anyone writing stuff in BASIC to have to check
each calculation to decide whether an overflow had occurred or not. If BASIC
generated a "Number too big" error for addition and subtraction of integers,
if you want to ignore them, all you need to do is to write a suitable error
handler.

The analogy with assembly language is not all that helpful, because you have
access to the flags which make it easy to check whether a calculation has
resulted in an overflow. BASIC's behaviour encourages people to be careless.

Locomotive BASIC on the Amstrad CPC generates on overflow error if you exceed
the 16 bits for integer calculations. This is absolutely necessary because
32768 is not a very bit number for an upper limit. I think Acorn only got
away with it because 32 bits gives pretty big numbers which are adequate for
most purposes.
--
Matthew Phillips
Durham
Martin
2020-01-07 11:20:11 UTC
Permalink
Post by Matthew Phillips
It would be an utter pain for anyone writing stuff in BASIC to have
to check each calculation to decide whether an overflow had
occurred or not. If BASIC generated a "Number too big" error for
addition and subtraction of integers, if you want to ignore them,
all you need to do is to write a suitable error handler.
Sorry, but I have to disagree!

Because a BASIC integer can be either signed or unsigned (and BASIC
does not know which) I do not think it is possible for BASIC to
generate any under/overflow errors for addition or subtraction.

Consider the following four examples - two for addition and two for
subtraction. Each example has the same numbers on the left shown as
signed, and on the right as unsigned hex.

You will see that each example has an obvious error case - BUT only
when used as either Signed or Unsigned, never both.

Signed Addition Unsigned addition
--------------- -----------------
2147483632 + 200 = -2147483464 &7FFFFFF0 + &C8 = &800000B8
Overflow set = Overflow Carry clear = OK

-16 + 200 = 184 &FFFFFFF0 + &C8 = &B8
Overflow clear = OK Carry set = Overflow


Signed Subtraction Unsigned Subtraction
------------------ --------------------
100 - 200 = -100 &64 - &C8 = &FFFFFF9C
Overflow clear = OK Carry clear = Underflow

-2147483516 - 2147483392 = 388 &80000084 - &7FFFFF00 = &184
Overflow set = Underflow Carry set = OK

The last example above is the same case as the third 'weird' example
from Alan's original post. It may look like an error if signed, but
perfectly logical if unsigned.

So, I think that it has to be the application which does any
necessary checks, as long as BASIC cannot tell whether an integer is
signed or unsigned.

... unless anyone can see the flaws in my argument!

Martin
--
Martin Avison
Note that unfortunately this email address will become invalid
without notice if (when) any spam is received.
David Higton
2020-01-07 15:05:34 UTC
Permalink
Post by Martin
Post by Matthew Phillips
It would be an utter pain for anyone writing stuff in BASIC to have to
check each calculation to decide whether an overflow had occurred or not.
If BASIC generated a "Number too big" error for addition and subtraction
of integers, if you want to ignore them, all you need to do is to write a
suitable error handler.
Sorry, but I have to disagree!
Because a BASIC integer can be either signed or unsigned (and BASIC does
not know which) I do not think it is possible for BASIC to generate any
under/overflow errors for addition or subtraction.
Consider the following four examples - two for addition and two for
subtraction. Each example has the same numbers on the left shown as signed,
and on the right as unsigned hex.
You will see that each example has an obvious error case - BUT only when
used as either Signed or Unsigned, never both.
Signed Addition Unsigned addition
--------------- -----------------
2147483632 + 200 = -2147483464 &7FFFFFF0 + &C8 = &800000B8
Overflow set = Overflow Carry clear = OK
-16 + 200 = 184 &FFFFFFF0 + &C8 = &B8
Overflow clear = OK Carry set = Overflow
Signed Subtraction Unsigned Subtraction
------------------ --------------------
100 - 200 = -100 &64 - &C8 = &FFFFFF9C
Overflow clear = OK Carry clear = Underflow
-2147483516 - 2147483392 = 388 &80000084 - &7FFFFF00 = &184
Overflow set = Underflow Carry set = OK
The last example above is the same case as the third 'weird' example from
Alan's original post. It may look like an error if signed, but perfectly
logical if unsigned.
So, I think that it has to be the application which does any necessary
checks, as long as BASIC cannot tell whether an integer is signed or
unsigned.
... unless anyone can see the flaws in my argument!
I'm in agreement with you, Martin. BASIC cannot know whether integer
variables are being used as signed or unsigned. It would be a real
pain if it decided that an error had occurred when really it hadn't;
the results of the calculation could never be seen. It would mean
descending into assembly language - which we /really/ don't want to
do.

I would point out that it is also legitimate to add a signed int to
an unsigned one, e.g. to add an offset in either direction to a pointer.
You can see that adding a negative offset to a top-bit-set address
would appear to overflow, when in reality no error occurs.

David
Matthew Phillips
2020-01-08 08:23:04 UTC
Permalink
Post by David Higton
Post by Martin
Post by Matthew Phillips
It would be an utter pain for anyone writing stuff in BASIC to have to
check each calculation to decide whether an overflow had occurred or
not. If BASIC generated a "Number too big" error for addition and
subtraction of integers, if you want to ignore them, all you need to do
is to write a suitable error handler.
Sorry, but I have to disagree!
Because a BASIC integer can be either signed or unsigned (and BASIC does
not know which) I do not think it is possible for BASIC to generate any
under/overflow errors for addition or subtraction.
Consider the following four examples - two for addition and two for
subtraction. Each example has the same numbers on the left shown as signed,
and on the right as unsigned hex.
You will see that each example has an obvious error case - BUT only when
used as either Signed or Unsigned, never both.
Signed Addition Unsigned addition
--------------- -----------------
2147483632 + 200 = -2147483464 &7FFFFFF0 + &C8 = &800000B8
Overflow set = Overflow Carry clear = OK
-16 + 200 = 184 &FFFFFFF0 + &C8 = &B8
Overflow clear = OK Carry set = Overflow
Signed Subtraction Unsigned Subtraction
------------------ --------------------
100 - 200 = -100 &64 - &C8 = &FFFFFF9C
Overflow clear = OK Carry clear = Underflow
-2147483516 - 2147483392 = 388 &80000084 - &7FFFFF00 = &184
Overflow set = Underflow Carry set = OK
The last example above is the same case as the third 'weird' example from
Alan's original post. It may look like an error if signed, but perfectly
logical if unsigned.
So, I think that it has to be the application which does any necessary
checks, as long as BASIC cannot tell whether an integer is signed or
unsigned.
My position is that BASIC sitting on the fence like this is ultimately not
much help to programmers who have a definite idea whether the values they are
dealing with are signed or unsigned, because in each case there are
circumstances in which you will not be told about overflows or carries. If
you're happy to be vague about whether the value is signed or unsigned, BASIC
might suit you.

Remember that although you have written "Carry set" and "Overflow set", BASIC
doesn't provide the programmer with access to this information (unless I'm
missing something).
Post by David Higton
I'm in agreement with you, Martin. BASIC cannot know whether integer
variables are being used as signed or unsigned. It would be a real pain if
it decided that an error had occurred when really it hadn't; the results of
the calculation could never be seen. It would mean descending into
assembly language - which we /really/ don't want to do.
I would point out that it is also legitimate to add a signed int to
an unsigned one, e.g. to add an offset in either direction to a pointer.
You can see that adding a negative offset to a top-bit-set address
would appear to overflow, when in reality no error occurs.
I agree that BASIC's behaviour is really useful when dealing with passing
flags to SWIs and handling memory addresses. But there are also many
situations in which not having an error generated is a nuisance. If you have
the address &ffff fffc and want to add -12 to get &ffff fff0 that's fine, but
what if you add 12? You'd want to know that you've wrapped round to zero
page.

I hope you can both agree that BASIC is inconsistent between add/sub and
multiplication. With addition and subtraction BASIC gives no errors if the
individual operands can all be expressed within 32 bits, whether the values
are signed or unsigned. But with multiplication, the values are firmly
Post by David Higton
a%=&7fffffff
b%=a%*2
Number too big
Post by David Higton
a%=&3fffffff
b%=a%*2
P. b%
2.14748365E9
Post by David Higton
a%=&C0000000
P. a%
-1.07374182E9
Post by David Higton
b%=a%*2
P. b%
-2.14748365E9
Post by David Higton
P. ~b%
80000000
Post by David Higton
c%=b%*2
Number too big

I know Dave argued that multiplication is different for signed and unsigned
integers in assembly language, but that's only the case if you're storing a
64 bit result from two 32 bit numbers.

There is further weirdness. Consider this, where the values are assigned to
Post by David Higton
a=&7fffffff
a=a+a
P.a
4.29496729E9
Post by David Higton
a=&7fffffff+&7fffffff
P.a
-2
Post by David Higton
a=2100000000+2100000000
P. a
-94967296
Post by David Higton
a=2100000000
a=a+a
P.a
4.2E9
Post by David Higton
a=2200000000+2200000000
P.a
4.4E9

In short, while I can understand what BASIC is doing, and can see that it is
useful in some special circumstances (dealing with flags and addresses) its
behaviour is unexpected if you are not au fait with assembly language and it
is full of traps for the unwary.

If BASIC gave you access to the overflow and carry indicators or allowed you
to declare variables as signed or unsigned it would be better. However, we
have to live with it as it is: I'm certainly not advocating changing it.

It's possible to avoid the 2100000000+2100000000 trap in a similar way to C
by writing 2100000000.0+2100000000.0

I only really got into Acorn machines with the Archimedes. Were integers on
the BBC B also 32 bit?
--
Matthew Phillips
Durham
Alan Adams
2020-01-08 10:26:50 UTC
Permalink
<huge snip>
Post by Matthew Phillips
I only really got into Acorn machines with the Archimedes. Were integers on
the BBC B also 32 bit?
I'm fairly sure that BBC micro BASIC integers were 16-bit. The highest
number was 32767, the lowest -32767
--
Alan Adams, from Northamptonshire
***@adamshome.org.uk
http://www.nckc.org.uk/
druck
2020-01-08 21:02:27 UTC
Permalink
Post by Alan Adams
<huge snip>
Post by Matthew Phillips
I only really got into Acorn machines with the Archimedes. Were integers on
the BBC B also 32 bit?
I'm fairly sure that BBC micro BASIC integers were 16-bit. The highest
number was 32767, the lowest -32767
No, Acorn never did anything in 16 bits, BBC Basic has always had 32 bit
integers. Have a play on JS Beeb https://bbc.godbolt.org/

---druck
Richard Ashbery
2020-01-09 12:53:38 UTC
Permalink
Post by druck
No, Acorn never did anything in 16 bits, BBC Basic has always had
32 bit integers. Have a play on JS Beeb https://bbc.godbolt.org/
What a great bit of nostalgia! Excellent research in finding it. Ironic
that you can't run the simulator in NetSurf!

Richard
Steve Fryatt
2020-01-09 23:16:41 UTC
Permalink
On 9 Jan, Richard Ashbery wrote in message
Post by Richard Ashbery
Post by druck
No, Acorn never did anything in 16 bits, BBC Basic has always had 32 bit
integers. Have a play on JS Beeb https://bbc.godbolt.org/
What a great bit of nostalgia! Excellent research in finding it.
It's been around a while: I found it back in 2018 whilst looking at the
archive of the BBC Computer Literacy Project, when I spotted Matt Godbolt's
name and then discovered that he mentioned one Richard Talbot-Watkins in the
emulator's credits... Two names that readers of AU's *Info back in the 1990s
should recognise.

There was a short article in the Wakefield newsletter about it at the time
-- the CLP Archive site is worth a visit for more nostalgia, IIRC. A little
worryingly, it turns out to have been September 2018... time flies.
--
Steve Fryatt - Leeds, England

http://www.stevefryatt.org.uk/
Steve Fryatt
2020-01-08 22:35:02 UTC
Permalink
On 8 Jan, Alan Adams wrote in message
Post by Alan Adams
Post by Matthew Phillips
I only really got into Acorn machines with the Archimedes. Were
integers on the BBC B also 32 bit?
I'm fairly sure that BBC micro BASIC integers were 16-bit. The highest
number was 32767, the lowest -32767
I'm pretty sure they weren't. On the Beeb and its 8-bit bretheren, BASIC
used 4 byte integers and 5 byte floats, just like now.

The Atom was a different BASIC, but the internet seems to think it also had
32-bit integers.
--
Steve Fryatt - Leeds, England

http://www.stevefryatt.org.uk/
Martin
2020-01-08 23:51:45 UTC
Permalink
Post by Matthew Phillips
I hope you can both agree that BASIC is inconsistent between
add/sub and multiplication.
[Snip]
Post by Matthew Phillips
If BASIC gave you access to the overflow and carry indicators or
allowed you to declare variables as signed or unsigned it would be
better. However, we have to live with it as it is: I'm certainly
not advocating changing it.
I agree it is inconsistent, but unless BASIC magically gains a
(backwards compatible) means of typing signed integers different from
unsigned, I am afraid we are stuck with it.

The only way I can think of is to add a new Function keyword which
after and add or subtract returned the 4 possible flags for
Signed/Unsigned Underflow/Overflow. But I am not sure if it would be
any real use.
--
Martin Avison
Note that unfortunately this email address will become invalid
without notice if (when) any spam is received.
Steve Drain
2020-01-10 11:30:03 UTC
Permalink
Post by Steve Drain
Post by druck
But anyway this highlights the lack of date and time manipulation
SWIs in RISC OS.
I approached this lack ages ago in Basalt, which has a comprehensive
set of datetime keywords. Maybe I could develop a module from this.
What SWIs do you think are lacking?

Does anyone else notice a lack of SWIs?

I have written quite a few time manipulation routines in assembler which
I would be happy to incorporate, but I need a feel for which direction
to go in. ;-)
Alan Adams
2020-01-10 12:38:11 UTC
Permalink
Post by Steve Drain
Post by Steve Drain
Post by druck
But anyway this highlights the lack of date and time manipulation
SWIs in RISC OS.
I approached this lack ages ago in Basalt, which has a comprehensive
set of datetime keywords. Maybe I could develop a module from this.
What SWIs do you think are lacking?
Does anyone else notice a lack of SWIs?
I have written quite a few time manipulation routines in assembler which
I would be happy to incorporate, but I need a feel for which direction
to go in. ;-)
Maybe a Date_Calc one, allowing arithmetic operations on 5-byte time
values.

ADD and SUB would be obvious. Maybe an option to return the result as
centiseconds, or minutes, or hours, days, weeks, months, years for
example. The centiseconds one would need to return in a 5-byte buffer. The
others could, for example, return as the "convert to ordinals" one.

Would reverse calculations be useful, returning a 5-byte date-time?

I'm not sure it would need to handle dates as strings - the Territory
module is the route for that.
--
Alan Adams, from Northamptonshire
***@adamshome.org.uk
http://www.nckc.org.uk/
Jean-Michel
2020-01-06 19:39:27 UTC
Permalink
Post by druck
Post by Martin
There is no way for BASIC to subtract two 5-byte integers, apart from
spliiting each to one 5-byte integer and one 1-bute - and handling
the case where the result is negative.
I'd always opt for a bit of assembler to do that, as its every easy to
do arbitrary sized arithmetic when you've got access to the CPU flags.
Post by Martin
However, if the difference is small, or you can ignore the lest
significant byte, you may be able to chose a single 4-bytes that will
provide the difference.
I'd never recommend that, no matter how sort the interval, an over flow
will occur at some point (the next in about 10 years from now).
But anyway this highlights the lack of date and time manipulation SWIs
in RISC OS. The only things you can do with a 5 byte time is convert it
to or from a string.
SWI Territory_ConvertTimeToOrdinals
return an integers block with centisecond, second, minute, hour, etc
PRM3 page 815
--
Jean-Michel BRÜCK (France)
Mél :***@orange.fr
Site : https://jeanmichelb.riscos.fr/
RiscOS 5.23 : RiscPc/IyonixPC/Raspberry/ARMX6.
Alan Adams
2020-01-06 21:13:09 UTC
Permalink
Post by Jean-Michel
Post by druck
Post by Martin
There is no way for BASIC to subtract two 5-byte integers, apart from
spliiting each to one 5-byte integer and one 1-bute - and handling
the case where the result is negative.
I'd always opt for a bit of assembler to do that, as its every easy to
do arbitrary sized arithmetic when you've got access to the CPU flags.
Post by Martin
However, if the difference is small, or you can ignore the lest
significant byte, you may be able to chose a single 4-bytes that will
provide the difference.
I'd never recommend that, no matter how sort the interval, an over flow
will occur at some point (the next in about 10 years from now).
But anyway this highlights the lack of date and time manipulation SWIs
in RISC OS. The only things you can do with a 5 byte time is convert it
to or from a string.
SWI Territory_ConvertTimeToOrdinals
return an integers block with centisecond, second, minute, hour, etc
PRM3 page 815
And it would be possible to do date-based arithmetic using this. Messy in
the general case, because of varying month lengths, and leap years. For
just elapsed time within a day or two, not too bad.

I did wonder whether switching to BASIC VI and putting the data into
floats would be a workaround. The floats in BASIC VI have more than 40
bits of precision.
--
Alan Adams, from Northamptonshire
***@adamshome.org.uk
http://www.nckc.org.uk/
Steve Drain
2020-01-08 12:46:50 UTC
Permalink
Post by Alan Adams
Post by Jean-Michel
Post by druck
But anyway this highlights the lack of date and time manipulation SWIs
in RISC OS. The only things you can do with a 5 byte time is convert it
to or from a string.
SWI Territory_ConvertTimeToOrdinals
return an integers block with centisecond, second, minute, hour, etc
And the other variants, now subsumed into Territory_ConvertTimeFormats
in RO5.

The Territory SWIs are not very friendly. ;-)
Post by Alan Adams
And it would be possible to do date-based arithmetic using this. Messy in
the general case, because of varying month lengths, and leap years. For
just elapsed time within a day or two, not too bad.
You also have 'Day of Week' (DOW) and 'Day of Year' (DOY) which might be
useful, but for extended periods you need the Julian Day Number (JDN).
There is a suitable algorithm on the Wikipedia page.
Post by Alan Adams
I did wonder whether switching to BASIC VI and putting the data into
floats would be a workaround. The floats in BASIC VI have more than 40
bits of precision.
With care I have used BASIC V floats to represent unsigned integer
values greater than &7FFFFFFF, to do things like DIV and MOD in special
cases. There is no universal way to do this, though.

In addition, a BASIC V float is adequate to hold a 5-byte UTC value. You
cannot do anything with it directly, but the '|' indirection operator
can copy it back into a buffer to be manipulated.
Loading...