0

Time ago I did a research trying to find a solution to calculate aspect ratio of a resolution, and this answer helped me to write the code below, which works fine for all common resolutions, but I just discovered that it fails to calculate the aspect ratio for many other resolutions, for example my code below will give a result value of "683:384" when trying to calculate the aspect ratio for "1366x768" resolution, which, according to Google results it is "almost" 16:9, so my calculation is wrong.

The aspect ratio of 1366x768 is 1366:768, which is almost 16:9, although more precisely 16:8.9956076

It fails for many other untypical resolutions.

My question is: what I'm doing wrong in the calculation of the aspect ratio and how do I fix it?.

EDIT: After researching a bit, I partially understood that the GCD approach is not viable for these kind of resolutions as mentioned here:

https://stackoverflow.com/a/10070517/1248295

However, having a table of "known display aspect ratios" as suggested in that thread I'm not sure if it is a viable solution, and I'm not sure how to apply that to the calculation.

Here is my current code:

''' <summary>Calculate the aspect ratio of the source resolution.</summary>
''' <returns>The resulting aspect ratio, expressed as X:Y.</returns>
Public Shared Function GetAspectRatio(resolution As Size) As Point
    Dim gcd As Integer = GetGreatestCommonDivisor(resolution.Width, resolution.Height)
    Return New Point((resolution.Width \ gcd), (resolution.Height \ gcd))
End Function

''' <summary>
''' Gets the Greatest Common Divisor (GCD) of one or more values.
''' <para></para>
''' That is, the largest positive value that divides each of the given values. For example, the GCD of 8 and 12 is 4.
''' </summary>
''' <remarks>
''' Wikipedia article: <see href="https://en.wikipedia.org/wiki/Greatest_common_divisor"/>
''' <para></para>
''' Original solution in C#: <see href="https://stackoverflow.com/a/41766138/1248295"/>
''' </remarks>
Public Shared Function GetGreatestCommonDivisor(ParamArray values As Integer()) As Integer
    Dim dblValues As Double() = Array.ConvertAll(values, Function(value) CDbl(value))
    Return CInt(dblValues.Aggregate(AddressOf InternalGetGreatestCommonDivisor))
End Function

Private Shared Function InternalGetGreatestCommonDivisor(first As Double, second As Double) As Double

    If (first < 0) Then
        first = Math.Abs(first)
    End If
    If (second < 0) Then
        second = Math.Abs(second)
    End If

    ' ReSharper disable CompareOfFloatsByEqualityOperator
    Do While (first <> 0) AndAlso (second <> 0)
        If (first > second) Then
            first = (first Mod second)
        Else
            second = (second Mod first)
        End If
    Loop
    Return If(first = 0, second, first)
    ' ReSharper restore CompareOfFloatsByEqualityOperator

End Function

Example usage:

Dim resolutions As Size() = {
    New Size(640, 480),
    New Size(800, 600),
    New Size(1280, 720),
    New Size(1920, 1080),
    New Size(1366, 768)
}

For Each res As Size In resolutions
    Dim aspectRatio As Point = GetAspectRatio(res)
    Console.WriteLine($"Resolution: {res.Width}x{res.Height}, Aspect Ratio: {aspectRatio.X}:{aspectRatio.Y}")
Next

That will produce this output:

Resolution: 640x480, Aspect Ratio: 4:3
Resolution: 800x600, Aspect Ratio: 4:3
Resolution: 1280x720, Aspect Ratio: 16:9
Resolution: 1920x1080, Aspect Ratio: 16:9
Resolution: 1366x768, Aspect Ratio: 683:384
ElektroStudios
  • 19,105
  • 33
  • 200
  • 417
  • 1
    See: [Algorithm for simplifying decimal to fractions](https://stackoverflow.com/questions/5124743/algorithm-for-simplifying-decimal-to-fractions) and [Coding challenge: convert a float to a fraction with a set limit on the number of digits](https://www.codeproject.com/Questions/1183278/Coding-challenge-convert-a-float-to-a-fraction-wit). – Olivier Jacot-Descombes Apr 11 '21 at 17:37
  • you can calculate the decimal approximation of 16/9 division (and 4/3 or any other "base reference", and if the result of your actual ratio division is close with some arbitrary precision, then just print that it's nearly 16/9 or 3/4 – Pac0 Apr 11 '21 at 17:38
  • @Olivier Jacot-Descombes sorry for my ignorance but I don't get how a decimal to fraction operation could help me to solve this, to get the value I need (x=16, y=9) from 1366,768 (width,height). – ElektroStudios Apr 11 '21 at 17:44
  • `(double)1366/768` --> `1.778645833`. Then convert this to a fraction by limiting the number of digits, instead of working with the GCD. – Olivier Jacot-Descombes Apr 11 '21 at 17:47
  • I'll try, thanks (to both). – ElektroStudios Apr 11 '21 at 17:47
  • I did not enough test with many resolutions, but it seems to work as expected. I used this algorithm: https://stackoverflow.com/a/45314258/1248295 with an accuracy of 0.01 – ElektroStudios Apr 11 '21 at 18:11
  • I'll rectify, it does not work (at least not that algorithm) for 16:10 (1440x900) and others, it gives 8:5 for this. – ElektroStudios Apr 11 '21 at 18:17
  • I really thought that obtaining the aspect ratio of a resolution would be as simple as knowing the right arithmetic algorithm to do so, but it seems just not possible for some resolutions, at least not with GCD algorithm or fractionizing it. So I'm finally doing a double check, with the gcd algorithm and the fraction algorithm, I compare both results and decide the right aspect ration based on a predefined list of resolutions, this way at least I ensure no errors with the known (common and uncommon) resolutions that I manage. – ElektroStudios Apr 11 '21 at 20:09
  • You can get [this one](https://stackoverflow.com/a/45314258/7444103) or [this one](https://stackoverflow.com/a/5128558/7444103) (~the same in your *scope*). The *precision* is of course ~`.005`. The second method can be converted to return a `Tuple` – Jimi Apr 11 '21 at 20:49

0 Answers0