Is my code suffering from an arithmetic error?

Click For Summary
SUMMARY

The discussion centers on a coding issue related to arithmetic errors in a C# function designed to approximate the value of π (pi) using fractions. The code utilizes the decimal type for precision but suffers from an integer division error when returning the result as a double. Specifically, the variables n and d are declared as long, leading to incorrect results when performing the division. The solution is to cast either n or d to double before the division to ensure accurate floating-point results.

PREREQUISITES
  • C# programming language knowledge
  • Understanding of data types: decimal, double, and long
  • Familiarity with mathematical concepts related to fractions and approximations
  • Basic knowledge of control flow in programming (loops and conditionals)
NEXT STEPS
  • Research type casting in C# to understand how to convert between long and double
  • Explore the decimal type in C# for precision in financial calculations
  • Learn about the implications of integer division in programming languages
  • Investigate optimization techniques for numerical methods in C#
USEFUL FOR

Software developers, particularly those working with numerical computations in C#, and anyone interested in improving their understanding of data types and arithmetic operations in programming.

SlurrerOfSpeech
Messages
141
Reaction score
11
What it does is described well in my comments. I'm using decimal, which is more precise than double but has a smaller range of values. https://msdn.microsoft.com/en-us/library/ms228360(v=vs.90).aspx

C:
        const decimal pi = 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034m;

        /// <summary>
        /// Finds n,d that minimize |pi - n/d|, where n is in the range 1, 2, ...
        /// and d is in the range [min, max]
        /// </summary>
        /// <remarks>
        /// Can assume min > 0 and min <= max
        /// </remarks>
        static double FindPiFraction(long min, long max, out long n, out long d)
        {
            // Smallest distance between a fraction and pi. Can initialize to Decimal.MaxValue for now.
            decimal smallest = decimal.MaxValue;

            // Placeholders for n, d to satisfy compiler. They are guaranteed to get set to other values below.
            n = -1;
            d = -1;

            // for each possible denominator
            for (long i = min; i <= max; ++i)
            {
                // Begin iterating through numberators. Begin at i * pi rounded down, e.g. if i = 1000
                // then we want to start at 3140. Iterate until our fraction is greater than pi. Keep track
                // of minimum distance between a fraction and pi, and keep track of the numerator and
                // denominator of that fraction.
                for(long j = (long)Math.Floor(Math.PI * (double)i); ; ++j)
                {

                    decimal fraction = (decimal)j / (decimal)i;
                    decimal diff = fraction - pi;
                    decimal distance = diff < 0 ? diff * -1 : diff;
                    if(distance < smallest)
                    {
                        smallest = distance;
                        n = j;
                        d = i;
                    }
                    if(fraction > pi)
                    {
                        break;
                    }
                }
            }
            ReduceFraction(ref n, ref d);
            return n / d;
        }
 
Last edited by a moderator:
Technology news on Phys.org
SlurrerOfSpeech said:
What it does is described well in my comments. I'm using decimal, which is more precise than double but has a smaller range of values. https://msdn.microsoft.com/en-us/library/ms228360(v=vs.90).aspx

C:
        const decimal pi = 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034m;

        /// <summary>
        /// Finds n,d that minimize |pi - n/d|, where n is in the range 1, 2, ...
        /// and d is in the range [min, max]
        /// </summary>
        /// <remarks>
        /// Can assume min > 0 and min <= max
        /// </remarks>
        static double FindPiFraction(long min, long max, out long n, out long d)
        {
            // Smallest distance between a fraction and pi. Can initialize to Decimal.MaxValue for now.
            decimal smallest = decimal.MaxValue;

            // Placeholders for n, d to satisfy compiler. They are guaranteed to get set to other values below.
            n = -1;
            d = -1;

            // for each possible denominator
            for (long i = min; i <= max; ++i)
            {
                // Begin iterating through numberators. Begin at i * pi rounded down, e.g. if i = 1000
                // then we want to start at 3140. Iterate until our fraction is greater than pi. Keep track
                // of minimum distance between a fraction and pi, and keep track of the numerator and
                // denominator of that fraction.
                for(long j = (long)Math.Floor(Math.PI * (double)i); ; ++j)
                {

                    decimal fraction = (decimal)j / (decimal)i;
                    decimal diff = fraction - pi;
                    decimal distance = diff < 0 ? diff * -1 : diff;
                    if(distance < smallest)
                    {
                        smallest = distance;
                        n = j;
                        d = i;
                    }
                    if(fraction > pi)
                    {
                        break;
                    }
                }
            }
            ReduceFraction(ref n, ref d);
            return n / d;
        }
Both n and d are declared as long, so n/d is computed using integer division, but you're returning a value of type double.

For example, if n is 19 and d is 10, the value returned will be the double value 1.0. This is very much a rookie error.
 
Last edited:
  • Like
Likes   Reactions: BvU

Similar threads

  • · Replies 2 ·
Replies
2
Views
2K
  • · Replies 2 ·
Replies
2
Views
2K
  • · Replies 54 ·
2
Replies
54
Views
5K
  • · Replies 2 ·
Replies
2
Views
2K
  • · Replies 100 ·
4
Replies
100
Views
13K
  • · Replies 49 ·
2
Replies
49
Views
12K
  • · Replies 125 ·
5
Replies
125
Views
20K
  • · Replies 52 ·
2
Replies
52
Views
13K
  • · Replies 3 ·
Replies
3
Views
7K
  • · Replies 48 ·
2
Replies
48
Views
13K