I think that perhaps we should ask the student to implement Division in this exercise. Here’s why.
The Exercise’s stand
The exercise’s instructions point out the following:
Despite Decimal being a custom type, we should still be able to treat them as numbers: the ==, <, >, +, -, and * operators should all work as expected on Decimals. For expediency, you are not required to implement division, as arbitrary-precision division can very quickly get out of hand. (How do you represent arbitrary-precision 1/3?)
Why the issue of arbitrary precision doesn’t translate to this exercise in particular
The exercise itself only asks the student to implement creation and arithmetic over this new type, Decimal
.
This Decimal
type needs to be:
- Buildable from a finite decimal expansion of a number (given as a
&str
in this case), and subsequently… - Be able to be added, subtracted and multiplied together, as well as
- Be able to be compared to other
Decimal
s, for both equality and ordering.
None of these properties actually require the type be able to represent the kind of precision implied in the instructions (a 0.3333333...
recurring decimal). The only case in which I could see such things becoming a problem is if we were to ask them to produce a String
representation of the Decimal
.
Recurring decimals can’t really arise from those 3 properties since we don’t ask for division, and recurring decimals can’t be created from addition, subtraction and multiplication of finitely writeable decimals. But if we were to ask for division, we still aren’t asking for the student to produce a String
representation of their Decimal
, so the possibility to create these decimals shouldn’t become a problem in the current state of the exercise anyways.
Okay, but then how would current solutions to this exercise be extended to include division?
With the exercise as given, the only way I see of solving it entails using fractions as the backing representation of a Decimal
, with BigInts
and BigUints
as the types for the numerator and denominator respectively. BigUints
for the denominator, as the sign need not be stored in more than one place.
If the student is using fractions then, division is very simple to implement: division by p/q
becomes simply a multiplication by q/p
. Since division in in a sense, fallible (the denominator q
should always be non-zero by construction, but the numerator p
could be zero, and division by zero is of course undefined), then we have the option of asking the student to return an Option<Decimal>
or to return a Decimal
and panic on division by zero. The example implementation of the Div
trait actually prefers to panic: Div in std::ops - Rust
Closing thoughts
Perhaps we should ask the students to implement division, as not asking to implement it is an artificial constraint over what is otherwise a good exercise in using the type and API they’ve just constructed. I think this is a good opportunity to let them grow a bit more from the Decimal
exercise overall.
As an aside, or food for thought: something that’s kind of interesting about the exercise, is that if the solution is implemented through fractions, they need not be in simplest form (ie such that the numerator and denominator are coprimes).
This does mean that the numerator and denominator might skyrocket if the implementer so chooses to allow… which I don’t think is a bad thing. Since making tight fractions isn’t necessarily the point of the exercise.
Though it could be a place for future exploration of bonus tests, perhaps? :D
Farewell.
– Félix
PS: (I’m adding this after having posted) I can implement all of this. I’d be glad to do so