What is the correct behaviour of @%=0 ?

Matrix Brandy defaults the print zone width to 10 if @% is zero, for example:

@%=0
PRINT 12.34

produces

     12.34

I have modelled BBC BASIC for SDL 2.0 (at least the versions coded in C) on this behaviour. But Acorn's BASICs (at least, what I have available in BeebEm and Red Squirrel) don't do this, the zone width appears to be set to zero and the output is left-justified:

12.34

So I'm now confused as to which is 'correct'. Brandy is usually very compatible with Acorn's ARM BASIC V, so I wonder if some versions do indeed set the width to ten. Can anybody provide a definitive answer?

Comments

  • Good spot, it's also incorrect upstream.

    There was an erroneous line in the source

    if (format == 0) format = STDFORMAT;
    

    I've removed it and it now does what it should be. I've also rebuilt the Windows nightly build.


  • https://distillery.matrixnetwork.co.uk:3004/discussion/comment/8#Comment_8 There was an error displaying this embed.

    I would ask you to revert that change until we've had a chance to debate the issue and come to some conclusion (which was the purpose of my message). If you simply change Matrix Brandy, it will mean that BBC BASIC for SDL 2.0 will be the sole outlier, and that would be a major dilemma for me (since I don't, and won't, make changes that might break existing programs without extremely careful thought - and nor should you IMHO).

    Clearly the statement:

    if (format == 0) format = STDFORMAT;
    

    is there deliberately, it's not "erroneous" in the sense that it can't have been 'accidentally' added. It must be there for a reason, and I'd like to try to establish what that reason is. My source has an almost identical statement, which I must also have added deliberately, which is probably significant.

    One possibility is that this issue has been discussed before, and that the consensus view was that this modified behaviour was so desirable that it outweighed the resulting incompatibility; perhaps a search of StarDot would be useful.

  • Indeed, a little more investigation shows that it's not straightforward. This code:

    @%=0
    PRINT PI
    

    gives different results even between Acorn's BASICs! In 6502 BASIC 2 it produces 3.141592653 whereas in ARM BASIC 1.84 it produces 3.1415926535 (one more significant digit); I'd be interested to know what it prints in ARM BASIC VI (64-bit floats).

    In the latest Matrix Brandy it produces 3.141592654 (back to 10 sig.figs. but not the same value as 6502 BASIC).

  • I'd be interested to know what it prints in ARM BASIC VI (64-bit floats).

    Answering my own question (I discovered that I have an old copy of BASIC VI) it prints this:

    3.1415926535897931
    

    which at 17 significant figures represents a precision that cannot be justified with 64-bit doubles.

    So maybe it was this inconsistency that led to a discussion about what exactly @%=0 should do, although I personally have no recollection of such a discussion (which means nothing at all, given my memory!).

  • BASIC VI v1.84 gives the same value you got.

    I don't recall seeing any discussion prior to my thread on what Brandy should do, if Dave Daniels ever had open discussions of what things should do, I have never found them.

    Interestingly, math.h (from GNU libc) defines the following:

    # define M_PI          3.14159265358979323846 /* pi */
    

    With a comment that it's not long enough for "long double", so provides another define that is apparently long enough for up to 128-bit floats:

    # define M_PIl         3.1415926535897932384626433832795029L /* pi */
    
  • Soruk
    edited January 2022

    Doing a bit of archaeology (with B-Em, which emulates enough Tubes it could masquerade as an organ!), it seems that back in the day you also had the zero space on @%=0. Which makes me then wonder, what made you decide to change? This is the Z80 Tube running CP/M 2.2.


  • [Richard Russell]
    edited January 2022

    I don't recall seeing any discussion prior to my thread on what Brandy should do, if Dave Daniels ever had open discussions of what things should do, I have never found them.

    OK. So we have a mystery as to why that statement is present in his code, I certainly don't believe that he added it on a whim or a momentary aberration! There must have been a reason.

    Neither my BASICs nor Brandy behave in a way that is compatible with Acorn's BASICs in respect of the way @%=0 affects the number of significant figures. I think that's probably a desirable deviation, but it does mean that compatibility with Acorn's BASICs isn't necessarily the best guide.

    Would it be helpful to consider the 'width' and 'sig.fig' bytes being zero independently, or only treat the entire @% value being zero as a special case?

  • Which makes me then wonder, what made you decide to change? 

    I wish I could remember! Generally I consider compatibility with my earlier BASICs (including the 32-bit Windows edition of BBCSDL) to be more important than compatibility with Brandy. The whole thing is very puzzling!

  • Would it be helpful to consider the 'width' and 'sig.fig' bytes being zero independently, or only treat the entire @% value being zero as a special case?

    With that line clobbered (it's still there, just commented out) I am indeed now treating each part being 0 independently - there is a separate statement that if significant figures is zero then use a default value of 10.

    [Z80...] I wish I could remember! Generally I consider compatibility with my earlier BASICs (including the 32-bit Windows edition of BBCSDL) to be more important than compatibility with Brandy. The whole thing is very puzzling!

    What does BB4W do? Of course, with your BASICs having been around a long time it only makes sense to look at Brandy compatibility if we were both implementing a new feature from scratch (like we did for the Teletext character set OSWORDs... false starts notwithstanding!!)

  • What does BB4W do? 

    BB4W and the 32-bit x86 editions of BBCSDL use virtually the same (assembler) code, so you can generally rely on them behaving identically. That's why wanting the other (coded in C) editions to behave differently is so puzzling.

    It seems that both Dave Daniels and I independently decided that changing the way a field width of zero behaves was so beneficial that it outweighed the incompatibility caused. But what could possibly have been that killer benefit, and why don't I have any recollection of it?

    You can see why I'm not at all happy simply to reverse that change.

  • OK, I've found a plausible explanation for why I did what I did - it's what my own documentation (wrongly) says happens:

    Zone and Print Field Width: Default 0A(10)
    

    So I'm thinking that rather than checking what my earlier versions actually did, I trusted my own documentation which says a field/zone width of zero causes a default value of 10 to be used!

    This documentation goes back a long way. The BBC BASIC (Z80) manual says exactly the same thing, so there may well have been a disparity between how BBC BASIC actually behaves and how it was documented to behave going right back to the early 1980s.

    So if I do decide to change the code, I will have to change the (current) documentation too.

  • You may want to revisit the change you made to Matrix Brandy today, because it's resulted in what I presume is an unintended side-effect. Pre today's change this code:

    @%=0
    PRINT PI
    

    resulted in:

    3.14159265
    

    (nine significant figures) whereas post the change it produces this:

    3.141592654
    

    (ten significant figures). All my BASICs default to nine significant figures in this case, which corresponds to the maximum number of reliable digits when using 40-bit floats, as in 6502 BASIC and ARM BASIC V.

  • Soruk
    edited January 2022

    The plot well and truly thickens. Even across versions for RISC OS (starting here with 1.84 which I have soft-loaded on boot) I get 11 significant figures in BASIC V, 17 in BASIC VI. I then unload and reload the ROM-based version in RISC OS 3.7 (version 1.16) and get 10 significant figures. That really doesn't help with trying to follow what Acorn are doing if it changes. This leaves me in a bit of a dilemma which to follow. Also, don't forget I'm using 64-bit floats as per BASIC VI (which Brandy has always done, despite earlier using the BASIC V tag instead of BASIC VI).

    Incidentally, I really should have looked here earlier, but history.txt (the changelog prior to the Matrix Brandy fork) contains this, for version 1.10 (28 May 2001):

    - Tidied up handling of @% in PRINT and STR$ when the number of digits to print is zero. '@%=0: PRINT PI' now produces 3.141592654 instead of 3, as per the Acorn interpreter.


  • Soruk
    edited January 2022

    OK, I've found a plausible explanation for why I did what I did - it's what my own documentation (wrongly) says happens:

    I'm not sure your document is wrong - but (maybe I'm just trying to read it while tired) I don't see it saying what happens if the value is 0, only what the default values are. And my reading of that is the default value is the value assigned at startup, &90A - which is used on the BBC, RISC OS (all BASIC versions) and Brandy (including Matrix Brandy), but can be changed by the user or program freely, and to reinstate default behaviour you would reset it to the default &90A value.

    I am not suggesting you change your BASICs, maybe just add a note in the documentation that setting PP or WW in @% to zero will result in the default values of 9 and 10 respectively being used, that it's different to the behaviour of Acorn and your historic Z80 and BBC BASIC(86) DOS implementations.

    Edit: BB4W 6.14a (trial) also doesn't indent for @%=0:

    I've also finally got BBCSDL 1.27a running on my 32-bit CentOS 6 (had to compile SDL2 from source, and an old version of SDL2_ttf that would compile on it!), and, curiously, when @%=0, there is no indent on 32-bit BBCSDL, but there is indentation on 64-bit (and console version, which you have only released in 64-bit).

    I'm starting to think it might be a bug in your C-source versions? I'm pretty sure you wouldn't want a behaviour difference between your C and Assembly versions.

  • [Richard Russell]
    edited January 2022

    I don't see it saying what happens if the value is 0, only what the default values are. And my reading of that is the default value is the value assigned at startup, &90A

    I agree that it's ambiguous. In a table that lists the effects of the individual bytes, I would interpret a column labelled 'default' as referring to those bytes in isolation. If all it is saying is that the initial value of @% is &90A then I would prefer it to be labelled initial rather than default, or to be omitted entirely.

    Indeed a Google search finds many hits for "difference between initial and default". In some contexts they are synonymous, but not always, and not here in my judgement. For example if you make @% LOCAL this will have the side-effect of setting it to zero, would you not then expect an action described as the default to apply?

    starting here with 1.84 ... I get 11 significant figures in BASIC V, 17 in BASIC VI. I then unload and reload the ROM-based version in RISC OS 3.7 (version 1.16) and get 10 significant figures

    Oh dear, that really complicates things, I had no idea it had changed in ARM BASIC. It's only very recently that I have updated the version of BASIC used here by Red Squirrel to 1.84 (you told me how to!). Is there such a thing as a publicly accessible change log for ARM BASIC that might shed some light?

    Just to add even more potential confusion, my BASICs (right back to the Z80 version) have always, by design. produced the same output from these two statements (assuming @% has the initial value &90A):

    PRINT ;PI
    PRINT STR$(PI)
    

    Acorn's BASICs don't produce the same result (STR$ gives one more significant digit) which I have always thought is crazy and counter-intuitive; I don't know of any other BASIC which behaves that way.

    Edit: In ARM BASIC VI (at least the version I have) STR$(PI) prints 3.1415926535897931; that has to be a bug. There's no way that STR$ (without overriding the formatting using the high-byte flag of @%) should produce so many digits, it would break large numbers of programs.

  • Soruk
    edited January 2022

    Is there such a thing as a publicly accessible change log for ARM BASIC that might shed some light?

    Not a changelog as such, but it seems like their GitLab repository allows narrowing down changes that only affect specific components:

    Edit: This commit looks like a possibility of the change (while I can't read ARM assembly the check-in comment does seem to address the length change.

    That, however, introduced a bug, so that got fixed here...


  • In ARM BASIC VI (at least the version I have) STR$(PI) prints 3.1415926535897931; that has to be a bug.

    Indeed the 'official' RISC OS documentation says this: "Byte 4, which can be 1 or 0, corresponds to the + STR$ switch. If this byte is 1, STR$ uses the format specified by the rest of @%. If it is 0, STR$ uses its default value of &00000A00". This "default value" explicitly specifies 10 significant figures, which is not what STR$ is doing in ARM BASIC VI:

    @%=&00000A00
    PRINT PI
    PRINT STR$(PI)
    

    According to the manual these two statements should behave identically, but they don't:

    3.141592654
    3.1415926535897931
    

    It's also worth noting that the RISC OS documentation distinguishes between initial value and default value of @%. The initial value is &0000090A whereas the quoted default value is &00000A00.

    So I conclude that Acorn's BASICs are not a particularly useful guide to what Matrix Brandy and my BASICs should do.

  • Soruk
    edited January 2022

    I think that bit of documentation is geared towards BASIC V (not VI) as the next bit shows an inaccuracy using &0A0A, which does not manifest itself in BASIC VI as it has more bits to play with. (The newer version of the document, posted as a PDF on riscosopen.org, leaves this inaccuracy out.)

    I've raised an issue on their forum concerning this here: https://www.riscosopen.org/forum/forums/4/topics/17046

    There is of course that dilemma, since, at least from what I am hearing, people (well, at least one does) use Matrix Brandy to run their old RISC OS programs natively under Linux so perhaps I should follow the more traditional BASIC V behaviour here? Is the trick to slavishly follow the documentation, or attempt to replicate what actually happens?


  • I think that bit of documentation is geared towards BASIC V (not VI)

    You're understandably looking at it from the perspective of an interpreter developer, my concern is for the BBC BASIC programmer! Unless it is for some reason unavoidable, two different implementations of BBC BASIC (whether for different CPU types, different floating-point formats or whatever) should ideally behave the same way. That's the very definition of a standardised programming language!

    Of course we know that it's far too late to achieve perfect compatibility. There are differences between Acorn's 6502 and ARM BASICs, and indeed between different versions of those, that can impact on the functioning of a program. There are even greater differences between Acorn's BASICs and mine. But nevertheless I would consider STR$ generating a ten significant-figure number in one implementation and a 17 significant-figure number in another completely unacceptable, and not something that can be 'documented away'.

  • But nevertheless I would consider STR$ generating a ten significant-figure number in one implementation and a 17 significant-figure number in another completely unacceptable, and not something that can be 'documented away'.

    Agreed, which is why I raised it as a bug on the ROOL forums, as it's not following what's been documented!


  • I'm starting to think it might be a bug in your C-source versions? 

    I've only just seen that comment (I think you must have added it in an edit). It's not a bug it's a difference, and it's that difference which caused me to create this thread! I've already said that all my versions of BASIC up to and including the 32-bit versions written in assembly language behave the same way, and all my versions based on the C-source behave in a different way.

    Until you modified Matrix Brandy, exactly the same difference was present between Acorn's BASICs and Brandy. That seems to me to be highly significant.

    We should look at all the available evidence, ascertain as far as is possible why those differences arose, and come to some conclusion about how best to proceed. That may or may not involve changes to Matrix Brandy or my BASICs - if we think it's appropriate for differences to remain, because 'correcting' them would cause more incompatibility that it would fix, that's fine so long as they are documented and understood.

  • Soruk
    edited January 2022

    Until Dave Daniels put that in Brandy back in version 1.10 no interpreter of BBC BASIC interpreted a zero field width as 10 (unless you had a C version back in 2001), so I think him fixing the bug where PP=0 would only print one significant figure he erroneously made it indent too. I'll see if I can track down a tarball of 1.09 (or if it can be extracted from his Sourceforge repo) to see what happened there.

    Edit: Sadly not, the CVS repository in Sourceforge is too new(!) and even the Wayback Machine isn't going far enough back on Dave Daniels' own site (there have been no changes to the page since it was first archived in 2005!) I'm trying to find something as far back as 2001.

  • [Richard Russell]
    edited January 2022

    no interpreter of BBC BASIC interpreted a zero field width as 10

    Ah, but unless I mis-read it (entirely possible) his modification wasn't in the case of a zero field width but a zero @%. That's potentially quite significant, because all four bytes of @% being zero will happen only in two circumstances that I can think of: setting @% to zero explicitly (in which case the programmer is relying on undefined behaviour, because that's illegal*) and @% being declared LOCAL.

    In which case it all starts to make much more sense to me. Making @% LOCAL is not uncommon, and a side-effect is to set @% to zero, a value it can never legitimately have*. So PRINT acting upon that otherwise illegal value the same as it does the default &90A format does seem like a valuable and justifiable change. It ought not to affect compatibility with any program compliant with the docs.

    So if that was indeed Dave Daniels's thinking, I agree with it and would support the modification remaining in Matrix Brandy.

    *Byte 1 (sig.figs. or decimal places) can only legitimately be zero if byte 2 (format) is 2, i.e. fixed point. If byte 2 is zero, byte 1 must be non-zero. Hence @% can't legitimately be zero.

  • I think my original question has now been satisfactorily answered. Here, in summary, are the conclusions I have reached:

    • Setting all 32-bits of @% to zero is illegal, because if byte 2 (the format type, NN) is zero byte 1 (the number of significant figures or decimal places, PP) must be non-zero. Both the RISC OS documentation and mine say that.
    • Hence the proper answer to the question "What is the correct behaviour of @%=0?" is "There is no 'correct' behaviour, it is undefined. Anything might happen".
    • It follows that setting @% to zero can legitimately do different things in different versions of BBC BASIC without introducing a formal incompatibility.
    • There is some value in Dave Daniels' modification because it means that after a LOCAL @% statement PRINT will behave the same as it would with the initial value of @% of &90A.
    • I would therefore recommend that his modification be restored in Matrix Brandy because there may be some programs which rely on it.

    I need to think further about my BASICs because I am currently acting on the field width (LS byte) being zero rather than the entire 32-bit value of @% being zero. That is definitely not right. One possibility would be to change the code to correspond with upstream Brandy's behaviour.

    Thanks for your help and investigations.

  • Soruk
    edited January 2022

    Glad I could help!

    Okay, I've reinstated that as a single-line change commit, so it's easy to find in the future (as I'm still not 100% convinced! With that line disabled it follows Acorn behaviour, sig fig length differences notwithstanding...)

    Other recent changes while looking at this are to change the exponent display to the way RISC OS and your BASICs show, rather than the C-style format (e.g. 1.2E7 instead of 1.2E+07), added @% to LVAR, showing in both hex and text form (RISC OS BASIC 1.84 only shows it as a string), and the decimal comma is now implemented following yours and Acorn's usage (it is on bit 23, Dave Daniels had the flag on bit 31 but never actually implemented it).

    It also looks like the RISC OS people have conceded that the documentation is inaccurate when it comes to BASIC VI.

  • Based on your release of Console edition V0.40, I have reinstated my original change, where @%=0 results in a default number of significant figures (which I've adjusted to follow ARM BBC BASIC VI, which uses 17 - the RISC OS folks have acknowledged it as a documentation deficiency rather than a bug in BASIC VI), and zero field width (effectively, left-justified).

  • Soruk
    edited January 2022
    Back across at the RISC OS forum, I've been pointed to the source code of BASIC V/VI, and I've found the equivalent in JGH's disassemblies of BASIC 1 and BASIC II. It certainly appears that the behaviour of precision=0 has a defined behaviour rather than a glitch in the code, it uses 9 in BASIC 1, 10 in BASIC 2 through to older BASIC V, 11 in newer BASIC V and 17 (code shows 18, but testing in RPCEmu shows 17 and 18 have the same results) in BASIC VI. There's no consideration for the entire @% being zero, the individual bytes are taken independently (though, checking the format type before deciding whether or not to replace precision 0 with the maximum).