1 × 1 is... 0.00379 ? Arithmetic challenges with mixed Metric and US units

Bottom line up front, I’ve halfway solved my problem, but I’m curious if there’s a better solution than the one I found.

I’m working on a moderately complicated simulation in Inform (a port of this old Applesoft BASIC program, and so it seemed obvious that I would want to start from the Metric Units extension and then add US units on top as needed.

Once I started getting numbers out, though, it was clear that there was something wrong. After fixing several of the more obvious bugs, this was the one that finally stumped me:

"Coolant Heat Flow Equation test" by Kevin Riggle

Include Metric Units by Graham Nelson.

[Gallons]
1.0 G (in US units, in G) or 1 gallon (in gallons, singular) or 2 gallons (in gallons, plural) or 1 gal (in gals, singular) or 2 gals (in gals, plural) specifies a volume equivalent to 3.785 liters.

[Volumetric flow]
Volumetric flow is a kind of value.
1.0 G/day (in US units, in G/day) or 1 gallon per day (in gallons per day, singular) or 2 gallons per day (in gallons per day, plural) or 1 gal/d (in gals/d, singular) or 2 gals/d (in gals/d, plural) or 1 gal/day (in gals/day, singular) or 2 gals/day (in gals/day, plural) specifies a volumetric flow.
Volumetric flow times elapsed time specifies a volume.

[Foo]
Foo is a kind of value.
1.0 sq gal/day (in US units, in sq gals/day) or 2 sq gals/day (in sq gals/day, plural) specifies a foo.
A volume times a volumetric flow specifies a foo.

When play begins:
	let CF be 1 gal/day;
	let CV be 1 gallon;
	let HF be CF times CV;	
	say "The result is [HF].[line break]".

Example Location is a room.

The expected result is, intuitively, 1 square gallon per day. What actually outputs is The result is 0.00379 sq gal/day.

(Gallons, squared, per day is a made-up unit but one that occurs as part of a larger formula in the source project.)

Finally getting a project that built, and looking at the Index in preparation for making this post, I discovered that Inform had developed some odd ideas about how the various units here are related, e.g.

volumetric flow x elapsed time = volume
    1 G/day x 1s = 1 cu m

volume x volumetric flow = foo
    1 cu m x 1 G/day = 1 sq gal/day

It looks to me like Inform is assuming that everything is defined in terms of the base unit, in metric. The easy answer was to stop using the Metric Units extension completely and pull in only the definitions from it I need, so that the volumetric flow relationship winds up defined as 1 gal/day x 1s = 1 gal, with no liter to confuse it, and probably that’s also the best answer for this particular project going forward, but the scientist in me is unsatisfied.

I think I had assumed that Inform has a much more robust idea of different systems of units than it actually has, and I’m surprised to discover that it isn’t at least picking the smallest defined unit in the same system.

Is there some way to hint to Inform that when I say, Volumetric flow times elapsed time specifies a volume, I mean it in gallons?

I’m not an expert on the units stuff, but I think that one (or both) of two things is going on:

  1. You’re running into a real number to integer conversion issue. (There are some known bugs with this in 6M62.)

  2. You’re confusing the units system by giving different notations different kinds of underlying units, even though they’re just different notations for the same kind of unit.

This is a modification that is producing a result of 1 square gallon per day, but I’m not sure that it’s doing what you want.

"Coolant Heat Flow Equation test" by Kevin Riggle

Include Metric Units by Graham Nelson.

[Gallons]
[1.0 G (in US units, in G) or 1 gallon (in gallons, singular) or 2 gallons (in gallons, plural) or 1 gal (in gals, singular) or 2 gals (in gals, plural) specifies a volume equivalent to 3.785 liters.]

1.0 G (in US units, in gallons) specifies a volume equivalent to 3.785 liters.

1 gallon (in gallons, singular) or 2 gallons (in gallons, plural) specifies a volume.

1 gal (in gallons, singular) or 2 gals (in gallons, plural) specifies a volume.


[Volumetric flow]

Volumetric flow is a kind of value.

[1.0 G/day (in US units, in G/day) or 1 gallon per day (in gallons per day, singular) or 2 gallons per day (in gallons per day, plural) or 1 gal/d (in gals/d, singular) or 2 gals/d (in gals/d, plural) or 1 gal/day (in gals/day, singular) or 2 gals/day (in gals/day, plural) specifies a volumetric flow.]

1.0 G/day (in US units, in gallons per day) or 1 gallon per day (in gallons per day, singular) or 2 gallons per day (in gallons per day, plural) or 1 gal/d (in gallons per day, singular) or 2 gals/d (in gallons per day, plural) or 1 gal/day (in gallons per day, singular) or 2 gals/day (in gallons per day, plural) specifies a volumetric flow.

Volumetric flow times elapsed time specifies a volume.

[Foo]
Foo is a kind of value.

1.0 sq gal/day (in US units, in square gallons per day) or 2 sq gals/day (in square gallons per day, plural) specifies a foo.

A volume times a volumetric flow specifies a foo.

When play begins:
    let CF be 1 gal/day;
    let CV be 1 gallon;
    let HF be CF times CV;	
    say "The result is [HF].[line break]".

Example Location is a room.

Hmm, I don’t think that’s it. If I look in the Index I still see entries like 1 cu m x 1 G/day = 1 sq gal/day, which isn’t right.

I based my definitions on the definitions from Metric Units, which varies the said unit in parentheses along with the given unit as I did, e.g.

1.0m (in metric units, in m) or 1 meter (in meters, singular) or 1 metre
(in metres, singular) or 2 meters (in meters, plural) or 2 metres (in
metres, plural) specifies a length.

I think if I wanted to make full use of Metric Units I would need to first define my units and relationships in metric and then define my US units in relation to the metric units, which was overkill for my use and feels like something Inform should be able to infer from my units being specified as in a completely different unit system.

Inform does seem to get itself confused in the conversions somewhere. Looking at the generated I6 code for that “when play begins” rule, we see:

! [2: let cf be 1 gal/day]

    		tmp_0 = 1065353216;
    ! [3: let cv be 1 gallon]

    		tmp_1 = 997723587;
    ! [4: let hf be cf times cv]

    		tmp_2 = (REAL_NUMBER_TY_Times(tmp_0, tmp_1));

And (ab)using a C++ compiler to convert those meaningless integers into floats, we discover that:

A gallon a day: 1.000000
A gallon: 0.003785
Result: 0.003785
Conversion Program
#include <stdint.h>
#include <stdio.h>

int main() {
    int32_t galday_src = 1065353216;
    float galday = *(reinterpret_cast<float *>(&galday_src));
    int32_t gallon_src = 997723587;
    float gallon = *(reinterpret_cast<float *>(&gallon_src));
    float result = galday * gallon;
    printf("A gallon a day: %f\nA gallon: %f\nResult: %f\n", galday, gallon, result);
    return 0;
}

So while the gallon is stored as 0.00375 cubic meters, the gallon per day (which doesn’t have a metric equivalent) is simply stored as 1.0 gallons per day. But then the two are multiplied with no further conversion, getting us our wrong result.

I guess you’ll have to either define everything in metric first and then define the imperial units in terms of the metric ones, or leave out the extension altogether and do everything from scratch in imperial units.

Or perhaps just convert the game to metric, while you’re at it. :wink:

OK, new theory. The way that the “Arithmetic” chart in the Index is produced suggests that the expectation is that your base units should always be expressed as 1 x 1 = 1, and that your specifications should be made accordingly.

It also seems that every notation linked to the same base kind is treated as an alternate representation of the same underlying value, unless an “equivalent” is specified. But even if an equivalent is specified, it seems that this does not automatically apply to unit arithmetic that involves ratios of units. [EDIT: changed “products” to “ratios” in preceding sentence – sorry for mixing up the terms]

When you say:

A volume times a volumetric flow specifies a foo.

Inform seems to be thinking in terms of base units of volume (cu m) and volumetric flow (G/day), and it is is assuming that 1 of the first times 1 of the second translates to 1 of the third. The conversion equation shows the “most basic” unit of each unit involved.

The “Empire Strikes Back” example in the Metric Units extension shows how equivalents can be phrased in terms of equivalents, so long as there is a chain of equivalents to the correct base unit. This seems helpful to save you work and give Inform the conversion data that it needs.

Is this better?

"Unit Problems II"

Include Metric Units by Graham Nelson.

[Gallons]
[1.0 G (in US units, in G) or 1 gallon (in gallons, singular) or 2 gallons (in gallons, plural) or 1 gal (in gals, singular) or 2 gals (in gals, plural) specifies a volume equivalent to 3.785 liters.]

1.0 G (in US units, in gallons) or 1 gallon (in gallons, singular) or 2 gallons (in gallons, plural) or 1 gal (in gallons, singular) or 2 gals (in gallons, plural)  specifies a volume equivalent to 3.785 liters.

[Volumetric flow]

Volumetric flow is a kind of value.

[1.0 G/day (in US units, in G/day) or 1 gallon per day (in gallons per day, singular) or 2 gallons per day (in gallons per day, plural) or 1 gal/d (in gals/d, singular) or 2 gals/d (in gals/d, plural) or 1 gal/day (in gals/day, singular) or 2 gals/day (in gals/day, plural) specifies a volumetric flow.]

1.0 cu m/s (in metric units, in cubic meters per second) specifies a volumetric flow.

1.0 l/s (in metric units, in liters per second) specifies a volumetric flow scaled down by 1000. 

1.0 cu m/day (in metric units, in cubic meters per day) specifies a volumetric flow scaled down by 86400.

1.0 l/day (in metric units, in liters per day) specifies a volumetric flow scaled down by 86400000.

1.0 G/day (in US units, in gallons per day) or 1 gallon per day (in gallons per day, singular) or 1 gal/day (in gallons per day, singular) or 1 gal/d (in gallons per day, singular) or 2 gallons per day (in gallons per day, plural) or 2 gals/d (in gallons per day, plural) or 2 gals/day (in gallons per day, plural) specifies a volumetric flow equivalent to 3.785 l/day.

Volumetric flow times elapsed time specifies a volume.

[Foo]
Foo is a kind of value.

1.0 he m/s (in metric units, in hectic meters per second)  specifies a foo. [1 hectic meter = 1 cubic meter * 1 cubic meter per second]

1.0 sq l/s (in metric units, in square liters per second) specifies a foo scaled down by 1000000. [1 cubic meter = 1000 liters; 1000 liters * 1000 liters / second = 1000000 square liters / second]

1.0 sq gal/s (in square gallons per second) specifies a foo equivalent to 14.326225 sq l/s. [1 gallon = 3.785 liters; 1 gallon per second = 3.785 liters per second; 3.785 liters * 3.785 liters per second = 14.326225 square liters per second]

1.0 sq gal/day (in square gallons per day) or 2 sq gals/day (in square gallons per day, plural) specifies a foo equivalent to 0.000011574 sq gal/s.

A volume times a volumetric flow specifies a foo.

When play begins:
    say "3 gal (default): [3 gal][line break]";
    say "1 cu m/s (default): [1 cu m/s][line break]";
    say "1 cu m/s (in l/s): [1 cu m/s in liters per second][line break]";
    say "1 cu m/day (default): [1 cu m/day][line break]";
    say "1 sq gal/s (default): [1 sq gal/s][line break]";
    say "1 sq gal/s in sq gal/day: [1 sq gal/s in square gallons per day][line break]";
    say "22826948 G/day (default): [22826948 G/day][line break]";
    say "22826948 G/day in l/day: [22826948 G/day in liters per day][line break]";
    say "22826948 G/day in l/s: [22826948 G/day in liters per second][line break]";
    let CF be 1 gal/day; [= 3.785 l/day = 0.000043808 l/s = 0.000000044 cu m/s]
    	let CV be 1 gallon; [= 3.785 l = 0.003785 cu m]
    let HF be CF times CV; [ = 3.785 x 10^-3 cu m * 4.3808 x 10^-8 cu m/s = 1.6581328 x 10^-10 he m/s = 1.6581328 x 10^-4 sq l/s]
    say "The result is [HF].[line break]";
    say "The result is [HF in square gallons per second].[line break]";
    say "The result is [HF in square gallons per day].[line break]".

Place is a room.

EDIT: Regarding your point about more automation in the conversion: I assume that you mean something like the ability to specify that:

  • the base unit of volume is the liter
  • the base unit of time is the second
  • 1 day is equivalent of 86400 seconds
  • 1 gallon is equivalent of 3.785 liters
  • a volume divided by a time is a flow

and have Inform automatically understand and interconvert units for flow like liters per second, liters per day, gallons per second and gallons per day. It can sort of do that in cases where the complex unit is multiplicative, but not when the relationship involves division, as here.

2 Likes

and have Inform automatically understand and interconvert units for flow like liters per second, liters per day, gallons per second and gallons per day. It can sort of do that in cases where the complex unit is multiplicative, but not when the relationship involves division, as here.

Yeah, that’s what I expected to happen. (And aren’t division relationships just the inverse of multiplication relationships? :wink: ) Or at least guessing that if I defined several kinds as belonging to US units that I would be defining them in relationship to each other where available and not metric units.

Trying your “Unit Problems II” and consulting the Index, it does look like now Inform correctly understands the relationships. A bit of work but if it’s necessary then it’s worth it! Thank you.

Yes. And that does seem like something similar to what the equation solver can already do. Who knows? Maybe the next release will include that functionality.

1 Like