Inform 7 does incorrect math on scaled units

Knocking around the UNITS system trying things, I’ve found at least two (seemingly, different) problems with Inform’s built-in math routines. The answer Inform gives is actually not mathematically correct. These are problems with very common operations like simple multiplication and even addition, so I would be surprised if these are not already known issues, thus I have not reported them yet as bugs and have been thinking about them for about a week wondering if I just miscalculated something, but I don’t think so. Math is math…

Both issues are with Inform’s system of fractional math. The first occurs when you simply scale a number with a non-base-10 value. For example…

[code]The Kitchen is a room. Filesize is a kind of value. 1KB specifies a filesize. 1MB specifies a filesize scaled up by 1024.

X, Y, and Z are filesizes that vary.

X is 500KB. Y is 700KB.

When play begins:
showme x;
showme y;
now z is x + y;
showme z;
now z is z - 700KB;
showme z.[/code]
The first value returned for Z is off – it returns 1.1729MB when the answer should be 1.1719MB – and the error is larger by an order of magnitude than the margin of error you would expect from the scaling (I would expect the 4th decimal place to possibly be wrong or omitted, but not the third, that’s too much error). However, it seems the value is being stored correctly, because subtracting 700KB from Z results in the correct value, so the mistake only appears when printing a scale-shifted result (and furthermore only when printing a result scale-shifted via a non-base-10 scale).

The other flaw is much simpler and I’d be surprised if people didn’t know about it already. Multiplying and dividing compound units (i.e. ‘6 foot 2’ or ‘8 lb 5 oz’) by each other produces results that are way off, usually by an order of magnitude – and the whole number, not just one digit. Witness the disastrous results of my attempt to create an (apparently) unitless, single-decimal-place number type…

[code]The Kitchen is a room. A single-decimal-place number is a kind of value.
1.9 specifies a single-decimal-place number with parts integer and decimal.
A single-decimal-place number times a single-decimal-place number specifies a single-decimal-place number.

When play begins:
let p be 1.9;
let q be 7.5;
let r be p * q;
let s be p / q;
showme r;
showme s.[/code]
Multiplication inflates the results when multiplying compound units together, in this case by a factor of 10. Division lowballs the result by a factor of I think either 100 or 1000 in this case; though I don’t remember specifically – you can probably work it out from the ‘To decide’ routine I ended up with to compensate (see below).

I was finally able to create a somewhat mathematically usable, compound unit, but after correcting for Inform’s errors, it isn’t exactly pretty. With multiplication it was easy: I just fudged it. With division the necessary precision gets lost, so the only way was to convert the whole thing to integer math and then back again (but something janky seems to be going on there, too, because I had to scale the dividend up by an order of magnitude more than I expected, to get the right answer out even using integer math, or maybe I just miscalculated at first, but anyway by trial & error, I basically just added factors of 10, until I got the right answer).

The below will probably have to be modified to compensate for a different level of error if you use non-base-10 scaling or if want greater than one decimal place of precision, but these additional ‘To decide’ routines or something like them seems necessary to get accurate co-multiplication or co-division out of a compound (i.e. two part) unit kind: please correct me if I am wrong…

[code]The Kitchen is a room.

A single-decimal-place number is a kind of value.
1.9 specifies a single-decimal-place number with parts integer and decimal.
A single-decimal-place number times a single-decimal-place number specifies a single-decimal-place number.

To decide which single-decimal-place number is (x - a single-decimal-place number) multiplied correctly by (y - a single-decimal-place number):
	let z be x * y;
	decide on z / 10.

To decide which single-decimal-place number is (x - a single-decimal-place number) divided correctly by (y - a single-decimal-place number):
	let xk be 10 * the integer part of x;
		now xk is xk + the decimal part of x;
		now xk is xk * 100;
	let yk be 10 * the integer part of y;
		now yk is yk + the decimal part of y;
	let zk be xk / yk;
		now zk is zk / 10;
	let zi be zk / 10;
	let zd be the remainder after dividing zk by 10;
	decide on the single-decimal-place number with integer part zi decimal part zd.

When play begins:
	let p be 1.9;
	let q be 7.5;
	let r be p multiplied correctly by q;
	let s be p divided correctly by q;
	showme r;
	showme s.[/code]

Paul.

EDIT: Damn, I have not tried this in 6G60 yet. I really should have — I will later.

If this is a problem in the new build, it sounds like bug report time.

For the second bit of code:

When play begins: let p be 1.9; let q be 7.5; let r be p * q; let s be p / q; showme r; showme s.
You have effectively said

When play begins: let p be 19 tenths; let q be 75 tenths; let r be p * q; [19*75=1425] let s be p / q; [19/75=0 (and some change, which is lost in the integer arithmetic)] showme r; [1425 is formatted as 142.5] showme s. [0 is formatted as 0.0]
so Inform has done everything correctly. Or, put another way, by writing

A single-decimal-place number times a single-decimal-place number specifies a single-decimal-place number.

you have claimed that a tenth times a tenth is a tenth.

Alright, did I do wrong by setting a kind of number to multiply by itself? That is not what the Writing With Inform manual suggests, I realise, and I had a feeling that might come up. Besides, my example is sort of bending the unit system to a purpose for which it wasn’t designed, and that makes it an unintuitive example.

So, let’s go by the book, then. As Writing With Inform recommends, let’s have a length times a length specify an area. But let’s make them compound units, and see if the math comes out with anything resembling common sense reality…

[code]The Kitchen is a room.

Length is a kind of value. 8 foot 11 specifies a length.
An area is a kind of value. 8 foot 11 inches square specifies an area.
A length times a length specifies an area.

X and Y are lengths that vary. Z is an area that varies.

X is 5 foot 4. Y is 4 foot 9.

When play begins:
now z is x * y;
showme x;
showme y;
showme z;[/code]
So now inform is telling me that when I multiply 5 foot 4 by 4 foot 9, the result is 304 feet 0 inches square. Once again, this is off by an order of magnitude, and seems to run counter to what the manual claims, which is that multiplying length units together is meant to represent an area. In order to achieve that end, Inform should have converted everything to inches, multiplied them together, and then divided everything by 12, and then by 12 again (because the result is squared) – but it forgot to divide by 12 the second time. Just as it forgot to divide by 10 a second time when we were dealing with a base 10 system instead of the 12 inches in a foot. Seems like a simple slip-up to me. It’s hard for me to imagine a situation where the current behaviour would be useful, even if by some odd technicality it proves to be correct. (I don’t really understand what you did there either Emacs with the tenth times a tenth being equal to a tenth, but I’d be curious if you think this new example is also working as intended.)

Paul.

EDIT: I just checked in the latest build (6G60) — the behaviour is unchanged. I’ll file a bug report using the above example (and probably a separate one, for the first bug regarding filesize calculations), assuming EmacsUser doesn’t convince me yet that I’ve got it all wrong. 8)

Hmm, I’m going to leave that last response up but I think Emacsuser is right. Because thinking about it my area specification implies that there are 12 square inches in a square foot, which is of course wrong – there are 144 inches in a square foot. So what happens if I specify that in my area? Suddenly I get the right result (the only thing I changed was the 8 foot 11 to an 8 foot 143)…

[code]The Kitchen is a room.

Length is a kind of value. 8 foot 11 specifies a length.
An area is a kind of value. 8 foot 143 inches square specifies an area.
A length times a length specifies an area.

Z is an area that varies.

X and Y are lengths that vary.

X is 5 foot 4. Y is 4 foot 9.

When play begins:
now z is x * y;
showme x;
showme y;
showme z;[/code]
I begin to see the light — thanks.

Maybe that first filesize bug is still worth reporting, though…

Paul.