[I7] Units and optional fractions - bug?

Hey everyone. I tried to report this to the Mantis bug tracker, but it seems to be down at the moment.

So whenever I declare a KOV to be used as a unit (such as 1.99 inch), it’s possible to be declared as having an optional tail (permitting us to write something like The width of the unreasonably wide screwdriver is 1 inch. The width of the coke can is 2.13 inches.).

But when I write out the value, the notation is displayed only when it obeys the full form. If we were to print out the two things, it becomes this:

The unreasonably wide screwdriver is 1 wide. The coke can is 2.13 inch wide.

What am I doing wrong here?

Can you post some code demonstrating more about what isn’t working for you? This works as I expect:

A distance is a kind of value.
2.0 inches (plural) specifies a distance.
1.0 inch (singular) specifies a distance.
A thing has a distance called the width.
The width of a thing is usually 0.5 inches.
The description of a thing is usually "It looks about [width] wide."

Laboratory is a room.
An unreasonably wide screwdriver is here.  The width of the screwdriver is 1 inch.
A coke can is here.  The width of the coke can is 2.13 inches.

Test me with "x screwdriver / x can".

(Having said this, the whole unit-recognition thing does seem to be surprisingly fragile. While writing this I got a ridiculous number of nonsensical errors, including an internal compiler error.)

1 Like
Nozzle width is a kind of value. 1.9 inches specifies a nozzle width with parts whole and fraction (optional, preamble optional).
Gas is a kind of value. The plural of gas is gasses. A gas has a nozzle width. 

The gasses are oxygen, methane. 
The nozzle width of oxygen is 2.0 inches. The nozzle width of methane is 1.5 inches.

A fuel generator is a kind of device. A fuel generator has a gas.
The liquid oxygen generator is an oxygen fuel generator. 

The liquid methane generator is a methane fuel generator. 

When play begins:
	repeat with g running through fuel generators:
		let x be the gas of g;
		say "[The g] provides [x] via a nozzle [nozzle width of x] in diameter."

There is a room.

When I run this, it yields

The liquid oxygen generator provides oxygen via a nozzle 2 in diameter.
The liquid methane generator provides methane via a nozzle 1.5 inches in diameter.

To elaborate: your solution yields the full value, complete with the .0. When we request that to be removed, it also removes the unit, but only under those circumstances.

So what this is doing is defining:

  • whole => 1
  • fraction preamble => .
  • fraction => 9 inches

So unless the fraction is present, inches isn’t present either.

I don’t think there’s any way (other than abandoning the idea of using multi-part numbers) you can specify that you want “inches” to apply to the value as a whole rather than just the fraction part; that’s just not how it works.

2 Likes

Having said that, it is possible to override the default printing rule to achieve what you want:

To say (W - nozzle width):
	if the fraction part of W is zero, say "[whole part of W] inches";
	otherwise say "[whole part of W].[no line break][fraction part of W] inches".

or

To say (W - nozzle width):
	say whole part of W;
	if the fraction part of W is not zero, say ".[no line break][fraction part of W]";
	say " inches".

The code does still contain the original printing rule and there might still be some circumstances where it gets called (anything iterating generically over properties, such as showme, being one of them), but this does seem to override the standard printing of it in this case at least, and should work for all cases where the compiler knows in advance that it’s about to say a nozzle width (which is most places).

So if you do go this way, it’s probably best to not define the fraction part as optional, so that when the default printing rule does get used it will show the full value properly.

1 Like

I think you nailed it, @mirality. Thank you for putting a spotlight on this!

Taking a look at the generated code (with comments added):

! Generic routine for printing nozzle width values
[ E67 
    value ! Implied call parameter
    which ! Implied call parameter
    rem ! internal use only
    S ! internal use only
    ;
    if (value == 0) jump Use_LP_0; ! these three all jump to same label so have no effect
    if (NUMBER_TY_Abs(value) >= 1) jump Use_LP_0;
    jump Use_LP_0;
    rtrue;

    ! 1.9 inches, scaling: x units --> 1 x + 0 stored at runtime (int) (defined as benchmark * 1)
    .Use_LP_0;                      ! the routine WILL get here
    print value/10;                 ! print "whole" part
    if ((value/1)%10 == 0) rtrue;   ! THIS IS PROBLEM LINE; no whole part means no "inches"
    print ".";                      ! print "." preamble
    print (value/1)%10;             ! print "fraction" part
    print " ";                      ! next two lines print units
    print "inches";
    rtrue;
];

Seems like it might be better at the end to follow a pattern like:

    if ((value/1)%10 == 0) jump Print_Units; ! always print units
    print ".";
    print (value/1)%10;
    .Print_Units
    print " ";
    print "inches";
    rtrue;
];

I would say that qualifies as a bug, @Eleas.

Actually, not really. This type of number was never intended to have units, which is why it doesn’t handle them.

What it’s designed for are values such as 5’4" or 3h27m18s – where obviously the suffix is included as part of the number preceding it.

It’d need some different syntax to explicitly designate a unit and/or suffix that’s always present. But you typically shouldn’t be using it for that purpose anyway – use a single real number instead of a multi-part number, it works better when doing calculations.

(See the Metric Units by Graham Nelson extension for a whole bunch of examples. Inches aren’t the best units to play with since they don’t really have a smaller unit that is in everyday usage.)

1 Like

Hmmm. They look like units, or at least unit abbreviations, but it’s handled under a separate subsystem. I see your point about the suffixes, though, and thank you for pointing out the distinction.

Taking a cue from Metric Units, @Eleas, does this do what you want?

"Unit Name Issue"

Length is a kind of value.

1 tenth-inch (in tenth-inches, singular) or 2 tenth-inches (in tenth-inches, plural) specifies a length.

1 inch (in inches, singular) or 2 inches (in inches, plural) specifies a length scaled up by 10.

Gas is a kind of value. The plural of gas is gasses. A gas has a length called nozzle width. 

The gasses are oxygen, methane, argon. 
The nozzle width of oxygen is 20 tenth-inches. The nozzle width of methane is 15 tenth-inches. The nozzle width of argon is 1 tenth-inch.

A fuel generator is a kind of device. A fuel generator has a gas.
The liquid oxygen generator is an oxygen fuel generator. 
The liquid methane generator is a methane fuel generator. 
The liquid argon generator is an argon fuel generator. 

When play begins:
    repeat with g running through fuel generators:
	    let x be the gas of g;
	    say "[The g] provides [x] via a nozzle [nozzle width of x in inches] in diameter." [note specification of "in inches" to prevent rendering the value in tenth-inches, which would be the default for argon]

There is a room.

Which yields:

The liquid oxygen generator provides oxygen via a nozzle 2 inches in diameter.
The liquid methane generator provides methane via a nozzle 1.5 inches in diameter.
The liquid argon generator provides argon via a nozzle 0.1 inches in diameter.

FYI, it does work if you specify it this way too:

The nozzle width of oxygen is 2 inches.
The nozzle width of methane is 1.5 inches.
The nozzle width of argon is 1 tenth-inch.

Purely for the sake of amusement I just experimented with writing an extension that is based on Metric Units (and so actually stores the values that way) but lets you write non-metric units in your source text and story output. Does that sound interesting enough that people might actually want to use it?

(This probably isn’t well-suited for casual usage, but it might enable something heavily simulation-based.)

The downside is that because the values are internally stored as metric you do end up with rounding errors:

	say "[3cm in inches]";			=> "1.1811 inches"
	say "[12lb in pounds]";			=> "12.00001 pounds"
	say "[12m in nonmetric units]";	=> "13.12336 yards"
	say "[22ft in ft]"				=> "21.99999ft"

While Inform does have the concept of rounding numbers, I haven’t found a way to combine that with unit-based numbers.

(This problem could be avoided by writing something not based on Metric Units but rather defining the units afresh. But I’m less inclined to do that myself since they’re not my native units in the first place.)

The first and third ones seem right to me, at least; what results were you expecting there?

I didn’t mean that they were incorrect, just that they’re shown to more decimal places than you probably would actually want.

Thank you. It kind of makes sense, but at the same time I feel the manual isn’t terribly clear on intended usage. The use case I have in mind (auto-generating names of items for another person’s project) is not a good fit for real numbers, I fear.

Regardless, thank you for looking into it. Several of the solutions here are succinct enough to make it work, anyway.