Fractions
Fractions copied to clipboard
Operations with unreduced fractions (WIP)
Following the discussion in #49 here is a POC with special handling for the operations of the non-reduced fractions.
Motivations:
- Given that all existing operations return a normalized fraction- the current code-base doesn't offer much use for the concept of a non-normalized fractions (other than the initial construction speed).
- There aren't many ways of reducing a decimal number (unless it has trailing zeros) - with the result having ~ the same size.
- Operations with
BigInteger
that have a trivial value (smaller thanint.MaxValue
) perform the same way, in which case using the GDC reduction on every operation becomes a lot more expensive. - When fractions are constructed using similarly-formatted numbers (e.g. with 2 decimal places) without normalizing the denominator, subsequent operations such as Add/Subtract are much faster
- Unless constrained, the
Multiply
andDivide
operations would eventually grow beyond the trivial size and become slow to work with.
I've tried to address most of these using the following strategy (breaking change):
Assuming:
NF = Non-normalized (improper) fraction
F = normalized fraction
⊙ = mathematical operation having two operants (+, -, *, /, %)
F ⊙ F = F
NF ⊙ F = NF
F ⊙ NF = NF
NF ⊙ NF = NF
I initially tried to address 5) by replicating the rules that are employed by the decimal type (these are the only docs I was able to find on the subject). However that didn't satisfy the constraints that I had set for myself:
-
x * 1000 / 1000 == x
should be true -
x / 1000 * 1000 == x
should be true -
x * 2 * 5
should be the same asx * 10
Note, that this isn't a strict requirement for 5) but, if possible, I wanted to be able to express the following concept:
1.00000 g * 1000 = 1000.0 mg
and returning back to 1.00000
when dividing by 1000
(as if the user is switching the balance display from grams to milligrams).
The reduction strategy for the multiplication is to:
ReduceTerms(ref thisNumerator, ref otherDenominator);
ReduceTerms(ref otherNumerator, ref thisDenominator);
return new Fraction(true,
thisNumerator * otherNumerator,
thisDenominator * otherDenominator);
While the Division
is just expanding:
return new Fraction(true,
thisNumerator * otherDenominator,
thisDenominator * otherNumerator);
Despite my best efforts, I wasn't able to balance the operations- everything i tried ended up failing one of 1) or 2) or 3) - if you can spot a possible reduction scheme that works, that would be great. Otherwise, I think we can live with the slight bias - the only degenerate case would be for the user to keep dividing a number by fraction that is smaller than 1 (which can be optimized by multiplying by the reciprocal)
- [x] Operations with non-reduced fractions should now produce non-reduced fractions (Add/Multiply/Divide) while preserving the number precision (denominator)
- [x] Added an optional
reduceTerms
parameter for (most) operations. - [ ] Add parameter to the
FromString
overloads - [ ] Possibly include a special string format that preserves the trailing zeros
More details coming soon..