Introduce conversion methods between Angle and Ratio
@angularsen, following your suggestions from #1337, here is an updated PR adding the conversion methods between Angle and Slope.
How do you suggest we proceed with creating strings like "1 in 2, 1 in 5, 1:2, 1:5" etc. ?
I could simply add the following method to Ratio.extra.cs:
/// <summary>
/// Converts the ratio to a string representing slope in a more detailed fraction format.
/// </summary>
/// <param name="cultureInfo">The culture info to format the string. If null, the current culture is used.</param>
/// <returns>A string representing the slope in a detailed fraction format like "3 in 4" or "3 : 4".</returns>
public string ToDetailedSlopeString(CultureInfo cultureInfo)
{
cultureInfo = cultureInfo ?? CultureInfo.CurrentCulture;
// Find the closest fraction to represent the slope
(int numerator, int denominator) = ConvertToFraction(this.DecimalFractions);
string slopeFormat = cultureInfo.Name == "en-US" ? "{0} in {1}" : "{0} : {1}";
return string.Format(cultureInfo, slopeFormat, numerator, denominator);
}
private (int, int) ConvertToFraction(double decimalFraction, int maxDenominator = 100)
{
if (decimalFraction == 0)
{
return (0, 1);
}
int sign = Math.Sign(decimalFraction);
decimalFraction = Math.Abs(decimalFraction);
int wholePart = (int)decimalFraction;
decimalFraction -= wholePart;
int lowerNumerator = 0, lowerDenominator = 1, upperNumerator = 1, upperDenominator = 1;
while (lowerDenominator <= maxDenominator && upperDenominator <= maxDenominator)
{
int middleDenominator = lowerDenominator + upperDenominator;
int middleNumerator = lowerNumerator + upperNumerator;
if (middleDenominator > maxDenominator) break;
double middleValue = (double)middleNumerator / middleDenominator;
if (decimalFraction < middleValue)
{
upperNumerator = middleNumerator;
upperDenominator = middleDenominator;
}
else if (decimalFraction > middleValue)
{
lowerNumerator = middleNumerator;
lowerDenominator = middleDenominator;
}
else
{
lowerNumerator = upperNumerator = middleNumerator;
lowerDenominator = upperDenominator = middleDenominator;
break;
}
}
// Choose the fraction that is closer to the original decimalFraction
double lowerDiff = decimalFraction - (double)lowerNumerator / lowerDenominator;
double upperDiff = (double)upperNumerator / upperDenominator - decimalFraction;
int finalNumerator, finalDenominator;
if (lowerDiff < upperDiff)
{
finalNumerator = wholePart * lowerDenominator + lowerNumerator;
finalDenominator = lowerDenominator;
}
else
{
finalNumerator = wholePart * upperDenominator + upperNumerator;
finalDenominator = upperDenominator;
}
return (finalNumerator * sign, finalDenominator);
}
I don't know what the correct term is, but I'd expect something like ToRatioString() to output on the format 1:3 for 1 to 3 ratio.
This could be a method just like you proposed above.
Then create some unit tests to verify the behavior for a range of values.
For example, how does it work when the value is almost but not exactly 1:3, like 1:2.999 ? I assume it would be represented as 1000:2999? Or should there be some option to perform rounding? I'm not sure.
There is some similar work here, for FeetInches.ToArchitecturalString():
https://github.com/angularsen/UnitsNet/blob/a95e33fafa466bf27706b54fd6ea9069a9dce97c/UnitsNet/CustomCode/Quantities/Length.extra.cs#L238-L314
Maybe it can be reused or take inspiration from.
This PR is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days.
This PR was automatically closed due to inactivity.