28Implementation
Define percent literals
Situation and starter reading
Percent stores tenths of a percentage point in a ten-bit field, while a source
literal denotes whole percent. The starter implements the Literal#(Percent) interface, but it
copies the source integer unchanged and treats the entire representation range
as the semantic domain.
Read the code in three passes: the struct establishes storage, fromInteger
establishes conversion, and inLiteralRange establishes which typed literals
are legal. Finally read show, where Percent sample = 37 supplies the type
context. Change only the two method bodies; preserve the struct, instance, and
observation module.
Despite the existing field name basisPoints and the runner's output label,
this exercise uses ten stored units per percentage point. Standard basis points
use one hundred units per percentage point. Preserve the supplied names and
runner; implement the exercise's ten-to-one scale, not a basis-point conversion.
Predict before editing
The starter compiles and Run reports a stored value of 37 for 37 percent. That is a unit-conversion error, not a width error. Its range predicate would also accept 101 and 1023 even though neither is a valid whole-percent literal for this domain.
Reason about two contracts separately. Conversion maps a source integer into
the stored unit. The range predicate answers whether that source integer
belongs to the percentage domain. bsc does not call a user-defined
inLiteralRange while compiling literals, so the predicate is a contract that
code calls explicitly, not an automatic compile-time check.
The ten-bit field has enough room for 1000 stored units, but its maximum bit pattern must not define the percentage limit.
Required change and observable completion
- Store ten units for each whole percentage point.
- Make
inLiteralRangereturnTruefor the inclusive range 0 through 100 andFalsefor the values just outside either boundary. - Keep the explicit
Literal#(Percent)instance and ordinary unsized literal syntax.
Run is observably correct when the sample declaration reports a stored value of 370. Completion also requires correct boundary behavior and conversion throughout the domain, not a special case for 37.
Progressive hints
Hint 1: separate the responsibilities
Only one method constructs Percent; only the other returns the Boolean range
answer. Do not mix unit conversion into the predicate.
Hint 2: follow the units
fromInteger receives whole percent as an elaboration-time Integer. Apply the
ten-to-one scale before converting into the field type.
Hint 3: test both edges
Build the range condition from a lower comparison and an upper comparison. Both documented endpoints belong to the domain; the neighboring integers do not.
Transfer experiment
After the valid implementation runs, add a temporary typed declaration at the
upper boundary and predict its stored field. Then move that literal one above
the boundary and predict again. The build still succeeds and stores 1010 units,
because bsc does not apply your inLiteralRange to the literal. Add a
temporary $display that calls inLiteralRange directly with 100 and 101 to
see the predicate itself reject the second value. Restore the original mkTop
afterward.