Skip to content

Commit 7e39e23

Browse files
authored
Add FeetInches.ToArchitecturalString() (#1106)
Fixes #1075 * Add FeetInches.ToArchitecturalString and tests * Fix broken link in readme
1 parent ab063b5 commit 7e39e23

File tree

3 files changed

+104
-1
lines changed

3 files changed

+104
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ Stargen is a decades old software to create realistic planets and satellites fro
424424
425425
https://github.com/ebfortin/primoris.universe.stargen
426426
427-
<img src="https://www.harringtonhoists.com/images/harrington_logo3.png" height="35">
427+
<img src="https://cdn.harringtonhoists.com/assets/harringtonhoists/logo-60629cc144429045d4c85740ab225e219add75b2c5c1e2c444ffa9500347a414.png" height="35">
428428
429429
#### Harrington Hoists, Inc. (A Subsidiary of KITO Americas, Inc.)
430430
> Harrington Hoists, Inc. is located in Manheim, PA, Elizabethtown, PA, South Holland, IL and Corona, CA. Harrington is a leading manufacturer of manual, electric and air chain hoists as well as wire rope hoists and crane products serving the North American material handling industry.

UnitsNet.Tests/CustomCode/LengthTests.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,5 +233,31 @@ public static void InverseReturnsReciprocalLength(double value, double expected)
233233

234234
Assert.Equal(expected, inverseLength.InverseMeters);
235235
}
236+
237+
[Theory]
238+
[InlineData(3, 2.563, 16, "3' - 2 9/16\"")]
239+
[InlineData(3, 2.563, 32, "3' - 2 9/16\"")]
240+
[InlineData(3, 2, 16, "3' - 2\"")]
241+
[InlineData(3, 2.5, 1, "3' - 2\"")]
242+
[InlineData(0, 2, 32, "2\"")]
243+
[InlineData(3, 2.6, 1, "3' - 3\"")]
244+
[InlineData(3, 2.6, 2, "3' - 2 1/2\"")]
245+
[InlineData(3, 2.6, 4, "3' - 2 1/2\"")]
246+
[InlineData(3, 2.6, 8, "3' - 2 5/8\"")]
247+
[InlineData(3, 2.6, 16, "3' - 2 5/8\"")]
248+
[InlineData(3, 2.6, 32, "3' - 2 19/32\"")]
249+
[InlineData(3, 2.6, 128, "3' - 2 77/128\"")]
250+
public static void ToArchitecturalString_ReturnsFormatted(double ft, double inch, int fractionDenominator, string expected)
251+
{
252+
var length = Length.FromFeetInches(ft, inch);
253+
254+
Assert.Equal(expected, length.FeetInches.ToArchitecturalString(fractionDenominator));
255+
}
256+
257+
[Fact]
258+
public static void ToArchitecturalString_DenomLessThan1_ThrowsArgumentOutOfRangeException()
259+
{
260+
Assert.Throws<ArgumentOutOfRangeException>(() => Length.FromFeetInches(1, 2).FeetInches.ToArchitecturalString(0));
261+
}
236262
}
237263
}

UnitsNet/CustomCode/Quantities/Length.extra.cs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,5 +236,82 @@ public string ToString(IFormatProvider? cultureInfo)
236236
// So inches are rounded when converting from base units to feet/inches.
237237
return string.Format(cultureInfo, "{0:n0} {1} {2:n0} {3}", Feet, footUnit, Math.Round(Inches), inchUnit);
238238
}
239+
240+
/// <summary>
241+
/// Outputs feet and inches on the format: {feetValue}' - {inchesValueIntegral}[ / {inchesValueFractional}]"
242+
/// The inches are rounded to the nearest fraction of the fractionDenominator argument and reduced over the greatest common divisor.
243+
/// The fractional inch value is omitted if the numerator is 0 after rounding, or if the provided denominator is 1.
244+
/// </summary>
245+
/// <param name="fractionDenominator">The maximum precision to express the rounded inch fraction part. Use 1 to round to nearest integer, with no fraction.</param>
246+
/// <example>
247+
/// <code>
248+
/// var length = Length.FromFeetInches(3, 2.6);
249+
/// length.ToArchitecturalString(1) => 3' - 3"
250+
/// length.ToArchitecturalString(2) => 3' - 2 1/2"
251+
/// length.ToArchitecturalString(4) => 3' - 2 1/2"
252+
/// length.ToArchitecturalString(8) => 3' - 2 5/8"
253+
/// length.ToArchitecturalString(16) => 3' - 2 5/8"
254+
/// length.ToArchitecturalString(32) => 3' - 2 19/32"
255+
/// length.ToArchitecturalString(128) => 3' - 2 77/128"
256+
/// </code>
257+
/// </example>
258+
/// <exception cref="ArgumentOutOfRangeException">Denominator for fractional inch must be greater than zero.</exception>
259+
public string ToArchitecturalString(int fractionDenominator)
260+
{
261+
if (fractionDenominator < 1)
262+
{
263+
throw new ArgumentOutOfRangeException(nameof(fractionDenominator), "Denominator for fractional inch must be greater than zero.");
264+
}
265+
266+
var inchTrunc = (int)Math.Truncate(this.Inches);
267+
var numerator = (int)Math.Round((this.Inches - inchTrunc) * fractionDenominator);
268+
269+
if (numerator == fractionDenominator)
270+
{
271+
inchTrunc++;
272+
numerator = 0;
273+
}
274+
275+
var inchPart = new System.Text.StringBuilder();
276+
277+
if (inchTrunc != 0 || numerator == 0)
278+
{
279+
inchPart.Append(inchTrunc);
280+
}
281+
282+
if (numerator > 0)
283+
{
284+
int greatestCommonDivisor(int a, int b)
285+
{
286+
while (a != 0 && b != 0)
287+
{
288+
if (a > b)
289+
a %= b;
290+
else
291+
b %= a;
292+
}
293+
294+
return a | b;
295+
}
296+
297+
int gcd = greatestCommonDivisor((int)Math.Abs(numerator), fractionDenominator);
298+
299+
if (inchPart.Length > 0)
300+
{
301+
inchPart.Append(' ');
302+
}
303+
304+
inchPart.Append($"{numerator / gcd}/{fractionDenominator / gcd}");
305+
}
306+
307+
inchPart.Append('"');
308+
309+
if (this.Feet == 0)
310+
{
311+
return inchPart.ToString();
312+
}
313+
314+
return $"{this.Feet}' - {inchPart}";
315+
}
239316
}
240317
}

0 commit comments

Comments
 (0)