Question about math and data types

This forum is dedicated to those who want support programming in ugBASIC.
Questo forum è dedicato a chi desidera supporto per programmare in ugBASIC.
Ce forum est dédié à ceux qui souhaitent prendre en charge la programmation en ugBASIC.
Post Reply
clovepower
Posts: 18
Joined: Sun Jun 23, 2024 7:59 am

Question about math and data types

Post by clovepower »

Hi

I was doing some experiments with math and floats and I found some things that looks inkonsistent to me, which makes some calculations impossible (as far as I understand).

I'd like to get your feedback on the below and a suggestion how I should do things differently, eventually.

What I would expect here would be (as in other languages):

1. If a variable is explicitly declared to be of a given type, it is created to be of that type, regardless how it is initialized.

2. The type of an expression is the "biggest" type of its operands (so 1/3.0 is a float expression and so it is a/10.0 even if a is an integer).

3. If a variable is not declared explicitly, either it is created with a default type (e.g. all variables are INTEGER) OR its type is determined by its initialization expression.

Thanks & Regards
Maxi

Code: Select all

a=10
b=a/3

REM a and b are treated as integers and int math is used
REM This is as expected
PRINT "a,b "; a,b

DIM c,d AS FLOAT
c=10
d=c/3

REM c seems to be treated as int while d seems to be float
REM Still, int math is used (d=3.0 and not 3.3333).
REM This is IMHO not as it should be as data type is explicitly declared,
REM regardless whether c and d are assigned to a int and float math should be used
PRINT "c,d "; c,d

e=10.0
f=e/3.0

REM e and f are treated as float and float math is used
REM This is as expected
PRINT "e,f "; e,f

REM So far, it seems type of variables is determined by the type
REM of their initialization value.
REM This makes DIM statement useless though (except for arrays)

g=10
h=g/3.0

REM g and h are treated as integers and int math is used
REM This is confusing to me, as I would expect h be float
REM (as it is assigned to a float expression).
REM It seems to be inconsistent with what happens above.
PRINT "g,h "; g,h

REM This raises an issue: If I have an expression where only int variables
REM are used, but I want that expression to be calculated using float math,
REM how can I do it?
REM In the example above I do want h to be 3.33333

i=10
DIM j AS FLOAT: j=i
j=j/3.0
DIM k AS FLOAT: k=i/3.0

REM This works for j but not for k
REM It seems this gives me a "workaround" for j
REM (which seems a bit inconsistent as it is initialized with an int, see c above)
REM but this means I need to "duplicate" my variables at each calculation
PRINT "i,j,k "; i,j,k
spotlessmind1975
Site Admin
Posts: 171
Joined: Fri Oct 06, 2023 8:25 pm

Re: Question about math and data types

Post by spotlessmind1975 »

Hi clovepower, and thank you for the message! :D
clovepower wrote: Wed Jul 24, 2024 6:54 am 1. If a variable is explicitly declared to be of a given type, it is created to be of that type, regardless how it is initialized.
You are definitely right! This is not only right, but it is precisely the behavior of ugBASIC. In the example you kindly shared, this behavior does not seem to emerge, but I believe it depends on an incomplete definition of the variables. In fact the line:

Code: Select all

DIM c,d AS FLOAT
should be read as:

Code: Select all

DIM c
DIM d AS FLOAT
Since c is not given any type, the default type is used, which is the signed 16-bit integer type (INT).
clovepower wrote: Wed Jul 24, 2024 6:54 am 2. The type of an expression is the "biggest" type of its operands (so 1/3.0 is a float expression and so it is a/10.0 even if a is an integer).
For this aspect I would first like to have a clarification. I would like to point out that the so-called "left hand" rule still applies today. In practice, whatever the expression in the calculation is, it is decomposed in such a way that the type of the "right" operand will be converted to that of the "left" operand. Errors and omissions excepted, this expression (dimensional calculation):

Code: Select all

a[int] = b[float] + ( c[int] + d[float] ) / e[float]
would become:

Code: Select all

a[int] = b[float] + ( c[int] + D[int] ) / e[float]
a[int] = b[float] + ( cD )[int] / e[float]
a[int] = b[float] + ( cD )[int] / E[int]
a[int] = b[float] + cDE[int]
a[int] = b[float] + CDE[float]
a[int] = bCDE[float]
a[int] = BCDE[int]
By introducing the promotion to the most bit extended type (from int to float, in this example) wouldn't we risk converting all calculations to float?
clovepower wrote: Wed Jul 24, 2024 6:54 am 3. If a variable is not declared explicitly, either it is created with a default type (e.g. all variables are INTEGER) OR its type is determined by its initialization expression.
Both things are true. :)

If a variable is used without having been previously assigned and without specifying its type with a suffix, it assumes the default type (INTEGER or that defined by the DEFINE DEFAULT TYPE pragma).

If a variable is used with a prefix (e.g. a%) then the type is defined by the suffix. Suffixes for integer types are described here: https://ugbasic.iwashere.eu/manual/datatypes#integers

If a variable is assigned the first time with a given expression, then the type of that variable will be the type of the expression, always applying the "left hand" rule.

Regards!
Marco
clovepower
Posts: 18
Joined: Sun Jun 23, 2024 7:59 am

Re: Question about math and data types

Post by clovepower »

Hi Marco

Thanks again, as usual, for your detailed and immediate response.
I apologize here as my reply is very pedantic as some things are a bit unclear to me :? , see below.
spotlessmind1975 wrote: Wed Jul 24, 2024 9:02 am
clovepower wrote: Wed Jul 24, 2024 6:54 am 1. If a variable is explicitly declared to be of a given type, it is created to be of that type, regardless how it is initialized.
You are definitely right! This is not only right, but it is precisely the behavior of ugBASIC. In the example you kindly shared, this behavior does not seem to emerge, but I believe it depends on an incomplete definition of the variables. In fact the line:

Code: Select all

DIM c,d AS FLOAT
should be read as:

Code: Select all

DIM c
DIM d AS FLOAT
ahhhhhh, OK, my bad then, I thought the float in my example would apply to both variables. Thanks for the clarification.
spotlessmind1975 wrote: Wed Jul 24, 2024 9:02 am
clovepower wrote: Wed Jul 24, 2024 6:54 am 2. The type of an expression is the "biggest" type of its operands (so 1/3.0 is a float expression and so it is a/10.0 even if a is an integer).
For this aspect I would first like to have a clarification. I would like to point out that the so-called "left hand" rule still applies today. In practice, whatever the expression in the calculation is, it is decomposed in such a way that the type of the "right" operand will be converted to that of the "left" operand.

...

By introducing the promotion to the most bit extended type (from int to float, in this example) wouldn't we risk converting all calculations to float?
OK, I was not aware of this rule. The few languages I know, and, as far as ChatGPT konows :D , BASIC dialects, use a different approach: the type of a single-operand expression (like a+b) is the "widest" between the operand types and operands are converted to the wider type, if necessary, before performing the calculation. So

Code: Select all

int i
long l
float f

i + l + f
is executed as

Code: Select all

(i + l) + f
type of (i+l) is long (wider than int); i is converted to long, + executes and returns long
type of (i+l)*d is float (wider than long); (i+l) is converted to float, + executes and returns float

Yes, this way each expression containing a float returns a float, but:

1. This should be the desired behavior, since you probably want to keep the highest precision in your calculations (what is the point of using float otherwise?). So, for example, you probably want a=1*PI to be 3.1415 and not 3.

2. With the left-hand rule, semantically equivalent expressions will return different results:

Code: Select all

r=1
p=3.14
a1 = r*r*pi
a2 = pi*r*r
so, a1 is 3 and a2 is 3.14....but they are the same thing.

3. One has to change the order of operands to maintain the desired type and this is not always possible.

Code: Select all

a=10
b=a/3.0
there is no way I have b to be 3.3333 unless i introduce a float variable (or is there a cast operator?)

Code: Select all

a=10
DIM af AS FLOAT af=a
b=af/3.0
4. Similarly, if I have an expression where only int variables are used, but I want that expression to be calculated using float math, how can I do it?

Code: Select all

i=10
j=3
DIM k AS FLOAT: k=i/j
I want k to be 3.3333 here...

Again, now that I understand the rule I can figure out expression type and code as needed, but I find this rule a bit unusual.
spotlessmind1975 wrote: Wed Jul 24, 2024 9:02 am
clovepower wrote: Wed Jul 24, 2024 6:54 am 3. If a variable is not declared explicitly, either it is created with a default type (e.g. all variables are INTEGER) OR its type is determined by its initialization expression.
Both things are true. :)
Yes, now that I understand the left-hand rule, I realize that what I see matches this behavior.

Thanks & Regards
Maxi
spotlessmind1975
Site Admin
Posts: 171
Joined: Fri Oct 06, 2023 8:25 pm

Re: Question about math and data types

Post by spotlessmind1975 »

Hi clovepower!
clovepower wrote: Wed Jul 24, 2024 3:50 pm ahhhhhh, OK, my bad then,
It's not like it was exactly a "mistake". In my opinion that is a question of interpretation. Some BASICs, if I remember correctly, allow you to define multiple variables with the same type. But, again in my opinion, it's not a very "BASIC" thing. It's more of a "Pascal" thing, so to speak. This is because that type of language does not include undefined variables, and therefore saves time typing. But in BASIC the advance definition is not mandatory.
clovepower wrote: Wed Jul 24, 2024 3:50 pm the type of a single-operand expression (like a+b) is the "widest" between the operand types and operands are converted to the wider type, if necessary, before performing the calculation.
Ok, I understood the point. I agree. Let's say that on the beta branch I will implement a mechanism like the one you suggest. Since it will modify the behavior of ugBASIC across the board, I can't put it on the main branch right away. As soon as I publish the beta update, I'll let you know. ;)
clovepower wrote: Wed Jul 24, 2024 3:50 pm 1. This should be the desired behavior, since you probably want to keep the highest precision in your calculations (what is the point of using float otherwise?). So, for example, you probably want a=1*PI to be 3.1415 and not 3.
Technically, the FLOAT format has lower precision than the INTEGER format, because not all numbers can be represented as a combination of 24 reciprocals of powers of two; on the other hand, 32 bit integer is able to represent 2^32 values exactly. And it is precisely for this reason that I considered promotion to FLOAT disadvantageous, at least if carried out implicitly. And, yes, you are right, it is absolutely strange to think that 1*PI = 3; however, writing f=3.33, and then doing f*2.0 to get 7.33 is also... strange!
clovepower wrote: Wed Jul 24, 2024 3:50 pm 2. With the left-hand rule, semantically equivalent expressions will return different results:
3. One has to change the order of operands to maintain the desired type and this is not always possible.
These are absolutely correct arguments, and they are the most convincing one, in my humble opinion.
clovepower wrote: Wed Jul 24, 2024 3:50 pm (or is there a cast operator?)
Casting exists, in fact, but it is not very flexible. And, by the way, it is explained by introducing a temporary variable so, in practice, it is a different way of writing your solution.
clovepower wrote: Wed Jul 24, 2024 3:50 pm 4. Similarly, if I have an expression where only int variables are used, but I want that expression to be calculated using float math, how can I do it?
Ok, but this is a different problem, where the FLOAT type is not the best solution, in my opinion, and for two good reasons: precision and speed. To explain better, suppose that the variable i varies between 0 and 100: now, you ask to have 3.33 as the result of 10/3. However, 3.33 can also be seen as 333/100, and therefore 10/3 can be seen as 10000/300. As you'll notice, they're all integers, which an 8-bit processor can manipulate quickly and precisely. This was also the reason why the FLOAT type is not the native type in ugBASIC.
clovepower wrote: Wed Jul 24, 2024 3:50 pm Again, now that I understand the rule I can figure out expression type and code as needed, but I find this rule a bit unusual.
Let's say that it is the rule that comes closest to assignment.
The alternative, that of the "right hand", is even stranger. :D

Regards!
Marco
spotlessmind1975
Site Admin
Posts: 171
Joined: Fri Oct 06, 2023 8:25 pm

Re: Question about math and data types

Post by spotlessmind1975 »

Hi clovepower!
clovepower wrote: Wed Jul 24, 2024 3:50 pm the type of a single-operand expression (like a+b) is the "widest" between the operand types and operands are converted to the wider type, if necessary, before performing the calculation
I'm happy to let you know that I just implemented this mechanism on the beta. If you try downloading the latest compiler, and recompiling your test source, I think everything will work as you expect. Please let me know if you find any problems and we will resolve them. 8-)
clovepower
Posts: 18
Joined: Sun Jun 23, 2024 7:59 am

Re: Question about math and data types

Post by clovepower »

Hi Marco

Once again, thanks for the informative and immediate response, as always. I do am amazed to see you already implemented this :D .

Only one thing...how do I access beta? :oops: Do I need to compile ugBASIC myself from source? I see in the "compilers" dialog in the UI there is a beta icon but it seems I cannot click it.

Regards
Maxi
spotlessmind1975
Site Admin
Posts: 171
Joined: Fri Oct 06, 2023 8:25 pm

Re: Question about math and data types

Post by spotlessmind1975 »

Hi clovepower!
clovepower wrote: Thu Jul 25, 2024 7:23 pm I do am amazed to see you already implemented this .
:mrgreen:
clovepower wrote: Thu Jul 25, 2024 7:23 pm Only one thing...how do I access beta?.
It is easy! :D

Click on Build, then on Options.
In the dialog click on Use BETA compiler.
Then click on SAVE.

From this moment on, the buttons to download the beta will be activated and you can download all the compilers by clicking on the button at the top right. The compilations will therefore take place with the beta version. And remember that, until you change this setting, you will be working with the beta version which may be less stable than the main version, even if it gives access to additional features.

Cheers!
Marco
clovepower
Posts: 18
Joined: Sun Jun 23, 2024 7:59 am

Re: Question about math and data types

Post by clovepower »

Hi Marco

Indeed I asked because I knew it must have been easy, like everything in the IDE ;) .

Unfortunately, I switched to BETA as you instructed, but am getting a compilation error now.

I will post it as an issue in GitHub, which I think is easier for you to track.

Thanks & Regards
Maxi
spotlessmind1975
Site Admin
Posts: 171
Joined: Fri Oct 06, 2023 8:25 pm

Re: Question about math and data types

Post by spotlessmind1975 »

Hi clovepower!
clovepower wrote: Sat Jul 27, 2024 8:53 am Indeed I asked because I knew it must have been easy, like everything in the IDE .
:D
clovepower wrote: Sat Jul 27, 2024 8:53 am I will post it as an issue in GitHub, which I think is easier for you to track.
Yep, it's been tracked and fixed.
But at the moment only for TRS-80 Color Computer 1/2/3 (as a hotfix).
For the rest of the targets it will be fixed in the next BETA UPDATE. ;)

Cheers!
Marco
Post Reply