Glulx VM spec 3.1.2

I’ve posted the complete Glulx 3.1.2 spec: eblong.com/zarf/glulx/

The floating-point opcodes are the big change. Minor changes, collected over the last several months:

Deprecated 8-bit and 16-bit locals. Also deprecated the use of the copyb and copys opcodes with a local-variable operand.
Noted that opcodes 0x1000 to 0x10FF are reserved for FyreVM; also gestalt selectors 0x1000 to 0x10FF, and I/O system 20.
Clarified that you may not use @setmemsize when the memory heap is active.
Clarified that glk_put_string() cannot accept encoded Glulx strings.

Please take a look (if you’re the spec-reading type) and tell me if you see anything obviously stupid.

The I6 compiler support for this float stuff is linked on the web page. (You’ll have to compile your own I6 binary, until this patch makes it into the official Inform distribution.)

The interpreter support (Glulxe and Quixe) is ready to go, but I don’t have it posted yet. I’ll try to do it tomorrow.

Great !

I just added the floating point instructions to ZMPP:

68 tests failed overall: floatmod (27), floatexp(6), fjump (30), fprint (6)

oh well, I guess, I still have some debugging ahead of me :smiley:

Wei-ju

From the spec itself, I’d never get all the special cases right. So maybe either refer to the unit tests from the spec or add tables of some more of the special cases.

For example, ceil(-0.5). My man page actually says,

which doesn’t match the behavior of the ceil function or the expected unit test behavior. It does happen to match what mono implements for the ceil function, which means I needed to add a test for this case. The Java spec describes how the ceil function is supposed to behave, which seems to agree with the unittests: http://download-llnw.oracle.com/javase/1.5.0/docs/api/java/lang/Math.html#ceil%28double%29

In the places where you do talk about the special cases, you write like

which seems obvious now, but somehow confused me greatly when I first read it (is that a hyphen between the positive and the pi?). If you could write this as

or

it might be a little clearer.

You might add a justification for fmod returning two values, as it seems kind of weird (fmodf only returns one value; it doesn’t match the integer opcode; in my implementation I end up calculating the mod twice, once for the mod and once for the quotient). And apparently “remainder” means something different from “modulus,” so you might not want to refer to “remainder” when describing this function.

Referring to the unit test seems like bad form, but you’re right, it’s probably necessary.

However, I can say that ceil and floor retain the sign of their argument. Which the libc ceil function does, on my Mac. I guess I’ll have to do some Linux tests before I release this.

(Not to mention, my makefile doesn’t include -lm,

Remainder vs modulus: I think it is the remainder, actually. A modulus would always have the same sign as the second argument.

The ± thing is copied straight from the Mac (that is, BSD) man pages. Sure, I’ll make it ±, I just have to dig into this twelve-year-old text-formatter program… :slight_smile:

The ANSI C draft that I have just says, “The ceil functions return the smallest integer value not less than x, expressed as a floating-point number.” which doesn’t seem to mandate positive or negative zero. Most of the floating point functions seem to be similarly vague about what happens in the corner cases, unlike your man pages which spell them out.

In the case of the ceil function, my libc behaves like yours, but my man page is wrong. I’ve switched to using the ceil(x) = -floor(-x) trick that the Java spec mentions.

I do see 5 test failures with the latest glulxe (running on Debian amd64): floatexp (1), floattrig (3), floatatan2 (1).

Apparently the IEEE 754 spec calls out a special remainder function. There’s some examples of the difference between remainder and modulus at http://msdn.microsoft.com/en-us/library/system.math.ieeeremainder.aspx. The unit tests are expecting the values described as “modulus” on that page. Java has a similar function (but without examples in their API docs). The wording here isn’t a big deal, but it’s one false path I took when trying to get my fmod opcode to match the unit tests.

Additionally, I don’t think the rules listed in the spec for the signs work out. Is the following right?

fmod -40.5 -3.5 m q
-> m = -2
-> q = -11
-> q * (-3.5) + m = 36.5, not -40.5

Perhaps the quotient shouldn’t copy the sign of the dividend?

I’ve added the comment about the unit test, the comment about ceil/floor, and the ± change to the spec document. I’m still looking at the modulo thing.

The problem on AMD64 appears to be very small (least-significant-bit) shifts when the underlying library is 64-bit. I see similar errors on OSX 10.6. I think the answer is just to admit that answers are not bit-identical between platforms, and adjust the tests to allow for this. (But I have not yet done that.)

The deal with modulo is that I wanted to unify the libc notions of fmod() and modf(). Probably wrongheaded, but they are both useful constructs and I figured there ought to be a clear parent function.

modf(x) returns the integer and fractional parts of x, both with the sign of x. fmod(x,y) returns x-ny, where n is (x/y) rounded towards zero.

(The Mac/BSD man pages explain fmod() terribly, which contributed to me getting this confused.)

When y is positive I’m fine. When y is negative, the problem you describe pops up, and your solution makes sense.

fmod(5.125, -2) => (-2, 1.125)
fmod(-5.125, -2) => (2, -1.125)

I will fix this tonight. Anybody implementing the spec, be aware that I’m changing @fmod out from under you. :slight_smile:

Thanks for noticing this.

For the jfeq opcode,

The unit test appears to be missing a test_jfeq(M_INF, M_INF, M_NAN), which should not branch, but it looks like glulxe is coded to take a branch.

You are right again, about jfeq/jfne.

I have updated the spec, the unit test, and Glulxe/Quixe to fix (and test) all the problems mentioned so far.

New lines in the spec:

“Float operations which produce inexact results are not guaranteed to be identical on every platform. That is, 1.0 plus 1.0 will always be 2.0, because that can be represented exactly. But acos(-1.0), which should be pi, may generate either 40490FDA (3.14159250…) or 40490FDB (3.14159274…). Both are approximations of the correct result, but which one you get depends on the interpreter’s underlying math library.”

“Speaking of special cases: I have tried to describe all the important ones for these operations. However, you should also consult the Glulxercise unit test (available on the Glulx web site). Consider it definitive if this document is unclear.”

@ceil/@floor: “The result keeps the sign of L1; in particular, floor(0.5) is 0 and ceil(-0.5) is -0. Rounding -0 up or down gives -0. Rounding an infinite value gives infinity.”

@fmod: “Perform a floating-point modulo operation. S1 is the remainder (or modulus); S2 is the quotient. S2 is L1/L2, rounded (towards zero) to an integral value. S1 is L1-(S2*L2). Note that S1 always has the same sign as L1; S2 has the appropriate sign for L1/L2.”

I’m passing the unit tests now, though my confidence that all the special cases work identically is small (… negative odd integers, negative zero, negative NaN, …).

I put together a small demo of floating point in Glulx at http://sourceforge.net/apps/trac/nitfol/changeset/231. Nitfol (allocating gobs of memory for every floating point operation) is about twice as slow as glulxe running this.

On the unit tests, glulxe still shows 2 failures, both of them because there needs to be tolerance on the answers:

log e=1.00000 or $3F800000 (should be 0.99999 or $3F7FFFFF FAIL) atan2(2,-0.5)=1.81578 or $3FE86B50 (should be near 1.81578 or $3FE86B51 FAIL)

With floating point in Glulx, and Glulx in Javascript, how far away can HTML5 canvas support be to replace graphics windows?

Yes, the tolerance there is too small – less than one ULP. I’ll adjust the tests.

I’m pretty sure I hit all the important cases in the test. (Except negative NaN, which I hope is not important.) If you think of any that I left out, let me know.

As for HTML5 canvas, I have a notion for getting inline HTML (or SVG) windows into the spec and changing events with the game. However, I need to hash through some blorb issues to make it feasible.

I’ve also posted the I6 compiler patch that lets you use floating-point constants in your I6 code. See my web site.

They use a “$+” or “$-” prefix, with the sign being mandatory: $+1, $-1, $+1.0, $-1.0e+0, $+1.0e+0

I’ve also defined constants FLOAT_NAN, FLOAT_INFINITY, FLOAT_NINFINITY.

(All of this is only when compiling a Glulx game.)

When writing the Mandelbrot code, the mistake I made the most was forgetting to call @numtof when I was introducing an integer to a floating point calculation. The philosophy of Inform seem to lack any type classification, but if you have any clever ideas for complaining about or coercing integers within floating point expressions, that would be great. This is a mistake that I can’t make in pretty much any language other than assembly, so it’s not one that I’m used to avoiding.

I think nitfol was more referring to implementing the existing Glk functionality with than introducing anything new…

I do think we need a new “webby” IO system, and I’d like there to be some competition in its design. That’s one reason why I’m keen to extend the Z-Machine, so we can also easily prototype new IO systems with Gnusto.

The Glk graphics window is very limited… draw filled rectangles or entire scaled images. That’s it. You can draw anything using lots of 1 pixel rectangles, but it’s not very nice. To my implementation, I’ve added line drawing, tooltips, and masked image drawing (all for map drawing, not exposed to Glulx). Things like text, gradients, anti-aliasing, curves, etc., would all be nice. If we’re focusing on a replacement for graphics windows, something like Cairo, Canvas, SVG, … seems like it would be a good match. These APIs put the pixels half-way between integers, so the lack of floating point in Glulx was a roadblock until now.

The second patch will help a little, as you can now say

@fadd x $+1.0 x;

That saves a @numtof and it’s at least a little more readable.

Anything beyond that requires adding a true type system to I6, which is not amenable to “a clever idea” – it’s a big, big change to the compiler. Vaporware worked through the problem a few years ago, but nobody’s used his code, as far as I know.

I’ve fixed the tolerance on the atan2 and log tests.

Also removed those debugging print statements from the I6 patch (inform631n-floatconst.patch) on my web site. Whoops.

I’ve posted Quixe-1.0.2 and Glulxe-0.4.6, which support the floating-point operations. On my web site, and in the Archive unprocessed directory.

The Glulx 3.1.2 spec seems to be fully baked. I added a couple of lines reserving opcodes for Dannii’s ideas, but there haven’t been any changes in specced interpreter behavior since the @fmod problem last week. I’m calling it done unless some other blatant mistake turns up.

eblong.com/zarf/glulx/quixe/quixe/play.html

That Quixe has some errors:
mod(-0,1)=rem 0.00000 or $0 (should be -0.00000 or $80000000 FAIL) quo -0.00000
mod(-0,-1)=rem 0.00000 or $0 (should be -0.00000 or $80000000 FAIL) quo 0.00000
mod(-0,1)=rem 0.00000 or $0 (should be -0.00000 or $80000000 FAIL) quo -0.00000
mod(-0,-1)=rem 0.00000 or $0 (should be -0.00000 or $80000000 FAIL) quo 0.00000

Browser info:
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 (.NET CLR 3.5.30729)

And are those tests actually identical? Or is there something different which it doesn’t output?

All tests pass for me with the latest Firefox:
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8 (.NET CLR 3.5.30729)