SPIN language and floats, longs and cast between them!
Charles Edmondson
Posts: 46
in Propeller 1
How does SPIN handle mixed types of data? I am working with a lot of different values, some of them appear to be longs, and some appear to be floats. but if I happen to mix types, I seem to get no answer at all! The FLOAT keyword says it applies only to CON objects and has no run time action. I need to test some values, but the software (with no errors!) seems to be ignoring the value. Anyone knows what is going on?
Comments
-Phil
x = 3.14159y = 0.33333
If you ever use those you will get the floating point representation of those numbers in binary.
But, Spin does not have floating point arithmetic so
z : = x + y
Ends up being performed as an unsigned 32 bit integer operation and the result is gibberish.
http://obex.parallax.com/object/229
There is no way that it fails due to changes in memory space, unless it is actually the stack you have allocated to it that is insufficient perhaps.
Almost certainly if there were such issues in the way Spin does division it would have been discovered by now. There has been a lot of people using this and analysing the Spin compiler, it's byte codes and it's interpreter over the years.
Cn1 := (( PackCap - ampSecUsed) * 100) / PackCap 'capacity at time n+1
So, if PackCap is larger than ampSecUsed, how could this be negative? yet, the displayed result is! When it reaches 60, it jumps to -16...
No, I don't make this up, I just have to deal with it :-(
x := ((y - z) * 100) / y
Certainly this can go negative if (y - z) * 100 oversflows a 32 bit signed number, as used by spin.
This is not a sufficient example. How are x, y and z defined and what values are assigned to them when this statement is run ?
Given that you hint at the use of time here, I start to think you have a traditional clock count overflow error.
As long as you obey the rules for integer math it works fine. Problems occur when you have values larger than 2,147,483,647 since these end up rolling over into the negative values.
Can you give us the numbers which produce the -16 value?
If you're running into the rollover issue, there are tricks to be done to get around this.
I missed this part of the earlier post. Everything gets flakey when memory is tight. You need to leave enough memory free for cog#0's stack. If the interpreter runs out of stack room, it will start overwriting your code.
There are lots of ways of freeing up memory. If you post your code I'm sure you could get some suggestions on how to free up some memory.
Like I said, it makes no sense whatsoever. it just does it...
Ok, looked again, and you are right. But, why does the code work fine now, but not when memory is tight?
As I mentioned earlier, when memory is tight, all bets are off. The stack is likely overwriting your code so all sorts of strange things can happen.
Hmm... "Charged to...". Sounds to me like you actually have to find that bug in your application rather than passing it off as a Spin problem.
So, never mind all that "proprietary" business. That does not preclude you from wrapping up the failing code, with variable initialization that causes the failure, into a PUB method that you can post here. You can replace the variable names to "x", "y", "z" but you have already given those away so there is no point.
I'm still betting on an overflow issue.
Quite likely the act of creating the requested example of failure will reveal the cause of the problem in your application as you try to recreate it !
You will be needing to check your stack usage of course.
I've lost count of how many times this has happened to me.
The act of isolating the problem code generally reveals where the real problem had been.
It's amazing how often one can get fixated on a bug having to be exactly here -> "Some innocent and harmless perfectly functioning line of code". Or in the library it is using, or in the compiler, or....
9 times out of 10 the problem ends up being some other dumb Smile error in some other place one casually decided was OK.
This problem smells of such a case.
We all do it.
You raise an interesting point. Many times I have given up the business of trying to read and analyse the source code trying to see the bug. Even stepping through code with a debugger or examining log output can be hopeless.
No, turn it around, let's not try to fix the problem, let's try to make it worse!
I call it "perturbation debugging". Often effective in finding those seemingly random timing race conditions or memory overuse.
The keywords Long, Word and Byte are only used when allocating static storage. Reads from Word- and Byte-sized statically allocated storage are zero-extended to 32 bits. Stores to Word- and Byte-sized statically allocated storage are truncated.
How else would it be possible to declare constants as floating point numbers?
Most "normal" arithmetic is done with two's complement integers. Except when, as you say, 8 and 16 bit values are coerced into positive 32 bit integers discarding their signedness. Which you might expect them to have because 32 bit values are always signed.
No wonder people get confused.
That's probably the simplest, most concise description of Spin's treatment of numbers I've ever read. It should be engraved in stone.
8- and 16-bit values do not have signedness to discard. They are always positive. For cases where they might be interpreted as being signed, there are the ~ and ~~ prefix operators.
As to floats being a type, no. They're just 32-bit integers that get treated as floats when using one of the floating-point libraries.
-Phil
If a language parses and accepts numbers written in the source code as floating point then there is a type that it accepts. Similarly for negative numbers. The fact that it proceeds to deal with them in irregular and unexpected ways is not obvious. (I know it's all in the Prop manual if you look hard enough)
In the following code: Compiling it offers no suggestion that negative WORDs and BYTEs don't exist. Or that the real type is a myth. But the types are surely there in the syntax.
Of course you and I know all that. ksldt is correct in his summary. We have seen many new comers to Spin tripped up by this confusion regarding types over the years though.
Your examples sent me back to the IDE. Surely, the byte and word lines would return a range error, right? Nope! In fact, you can even define them with positive numbers that are out of range. But if you try to define a byte or word with a floating-point value, you get an error.
-Phil
Turns out though BST gives no error for floating point declarations of BYTE and WORD
Openspin does object to floating point values in BYTE and WORD. The error message makes no sense though:
Then there is this: What? 128 is not bigger than 38 all of a sudden.
And my favourite piece of Spin type weirdness. What sequence of bytes does this produce: ?
-Phil
I always wondered about this phenomena:
The fact that doesn't concatenate the two strings makes perfect sense. Spin has no string concatenation operator, and it certainly isn't '+'. Javascript is freaky for making operators do whatever arbitrary operation the standards committee felt like, while in Spin, operators consistently do the same thing: zero-extend their operands to 32 bits, do the operation integer-wise, and then truncate the result to the storage size. A string in a DAT section is the same as a list of characters. Therefore, ends up as , which ends up as
Is there any way to do inline code with BBCode? Similar to `code` notation in Markdown?
According to BST, they're both little endian.
I have never decided if I like the use of "+" as a string concatenation operator in any language. It makes perfect sense for Spin not to have a string concatenation operator, that would get us into the world of dynamic memory allocation and garbage collection (Except in this case of static memory allocation in DAT sections).
However what actually happens with "+" and strings here is unexpected and as far as I know never useful (Perhaps for some sneaky code obfuscation contest ). Basically it nuts and it would be better to generate a syntax error.
Yes, Javascript is freaky. Hardly surprising as it was designed and prototyped in 10 days and shipped weeks later! It's creator, Brendan Eich, wanted to fix a lot of that freakyness when Netscape took it to ECMA for standardization, sadly Microsoft barged in to the standards committee and said no. They would have had to make changes to their faithful clone of the original. We can blame MS for the freakyness of JS
Spin operators do consistently do the same thing. Sadly that means LONGs are always signed but WORD and BYTE are unsigned. That is an inconsistency in itself. The programmer has to know and deal with it accordingly.
We must have different BSTs. That example I posted above compiles to: Note the different endianness in code and in DAT. Almost never a problem I guess and I can see why it might be done that way. Still a bit odd.
Or is BST's display of bytes in the listing backwards somewhere?
Edit: Seems I labelled BIG and LITTLE backwards. I fixed it.
An immediate value inside the bytecode can be encoded in several ways to minimize the bytecode size. Try for example:
x := $8000000
So you anyway can not access constants in bytecodes, only the Spin interpreter does this. And the interpreter knows how the value is stored.
It would be fatal if operators like '+' would generate an error inside DAT sections. You really need to be able to do calculations at complie time for data elements.
If you write "Hello" + "World" then it's your fault, every language has its rules. Just use a comma instead of '+' to concatenate the "strings". After byte follows just a list of comma separated bytes in various formats. The "Hello" is just a simplification for "H","e","l","l","o" and a very handy one.
You can use signed bytes and words if you need them. Just add a prefix to the variable (~ or ~~). Yes it's different from other languages with types, but for me so much more logical.
Andy
The "Hello" + "world" thing only surprised me years ago when I was writing my own PASM assembler. The idea was to support the syntaxes of VAR, CON and DAT blocks. I was busy trying out all kinds of syntax error cases in the Prop Tool to see what happened.
Certainly "+" and other operators should work in DAT blocks. It's just that "Hello" + "World" thing that perhaps should produce a warning/error. It is rarely (never?) useful to do and generally a mistake like any other syntax error. So why not?