I am currently working on a fraction datatype that is written in C#10 (.NET 6).
It is based on two BigIntegers in the Numerator and Denominator, which means that this fraction basically has infinite precision.
The only problem that now arises is memory consumption.
The Numerator and Denominator both get really big quickly, especially when roots are calculated using Newton's method.
An example for this using Newton's method to calculate the square root of 1/16:
x_0 = 1/16
x_1 = 17/32
x_2 = 353/1088
x_3 = 198593/768128
x_4 = 76315468673/305089687808
x_5 = 11641533109203575871233/46566125024733548077568 (≈0.2500000398)
The results get accurate quickly but the "size" of the fraction almost doubles every iteration.
When more precise results are required, the memory usage is just way too high.
A solution to this problem is rounding.
I want the user to be able to set a maximum error which the rounded fraction should be in.
My approach was using Euclid's GCD algorithm but modified for this max error clause:
public static Fraction Round(Fraction a, Fraction error)
{
BigInteger gcd = GCD(Math.Max(a.numerator, a.denominator), Math.Min(a.numerator, a.denominator), error);
return new Fraction(){ numerator = RoundedDivision(a.numerator, gcd), denominator = RoundedDivision(a.denominator, gcd) };
}
public static BigInteger GCD(BigInteger a, BigInteger b, BigFraction maxError) =>
new Fraction(){ numerator = BigInteger.Abs(b), denominator = 1 } <= maxError ? a : GCD(b, a % b, maxError * a);
// Rounds an integer division: 0.5 -> 1; -0.5 -> -1
public static BigInteger RoundedDivision(BigInteger a, BigInteger b) => (a*2 + a.Sign * b.Sign * b) / (2*b);
The results looked promising at first, but i ran into cases where the rounded result was outside the specified error.
An example for this using pseudo-code:
value = 878/399 (≈2.12531; value is represented as a fraction of two integers, not the division result)
error = 1/8 (=0.125)
Round(value, error) = Round(878/399, 1/8) = 2 (=2/1)
2 is outside of the specified max error by ~0.00031 (878/399 - 1/8 ≈ 2.00031 => 2 shouldn't be possible).
Correct results would be eg. 2.125 or 2.25, because these values lie in the range of 878/399 ± 1/8.
Does someone know why this is happening and how can i fix this?
EDIT: The result of the rounding should still be a fraction.