Pages

Tuesday, April 24, 2018

How to convert a decimal to a fraction in X++ with Dynamics AX or Dynamics 365 FO

I had a customer who needed to convert a decimal to fraction, so I just translated this StackOverflow post where all credit is due. I'm sure someone else will find it useful at some point.

The two main things in the job below (details taken from StackOverflow) are "_real", which is the value you want to convert, and "_accuracy", which specifies the max relative error; not the max absolute error.

So _accuracy = 0.01 would find a fraction within 1% of the value.

I also quickly threw this job together, so I didn't test for extremely large integers or any edge cases.

Here's a simple job that demonstrates how it works.


static void KW_DecToFrac(Args _args)
{
    // Create a function that accepts these two parameters
    real                _real           = 0.45;
    real                _accuracy       = 0.01;
    
    int                 sign = _real < 0 ? -1 : (_real == 0 ? 0 : 1);
    real                maxError;
    System.Decimal      d;
    
    int n;
    int lower_n = 0;
    int lower_d = 1;
    int middle_n;
    int middle_d;
    int upper_n = 1;
    int upper_d = 1;
    int z;
    
    void f(int _N, int _D)
    {
        info(strFmt("%1/%2", _N, _D));
    }
    
    _real = abs(_real);
    
    maxError = sign == 0 ? _accuracy : _real * _accuracy;
    
    d = System.Math::Floor(_real);
    n = System.Decimal::ToInt32(d);
    
    _real -= n;
    
    if (_real < maxError)
    {
        f(sign * n, 1);
        return;
    }
    
    if (1 - maxError < _real)
    {
        f(sign * (n+1), 1);
        return;
    }
    
    while (true)
    {
        z++;
        middle_n = lower_n + upper_n;
        middle_d = lower_d + upper_d;
        
        if (middle_d * (_real + maxError) < middle_n)
        {
            upper_n = middle_n;
            upper_d = middle_d;
        }
        else if (middle_n < (_real - maxError) * middle_d)
        {
            lower_n = middle_n;
            lower_d = middle_d;
        }
        else          
        {
            f((n * middle_d + middle_n) * sign, middle_d);
            return;
        }        
    }
    
    info("Done");
}

No comments:

Post a Comment