On Sunday, 27 August 2017 at 19:47:59 UTC, Alex wrote:
[..]
Is there a workaround, maybe?

To expand on the earlier workaround: You can also adapt a floating point to string algorithm in order to dynamically determine an upper bound on the number of after decimal point digits required. Below is an untested adaption of the reference C implementation of errol0[1] for that purpose (MIT license as that is what the original code is under):

---
void main()
{
    assert(gcd(0.5, 32) == 0.5);
    assert(gcd(0.2, 32) == 0.2);

    assert(gcd(1.3e2, 3e-5) == 1e-5);
}

template gcd(T)
{
    import std.traits : isFloatingPoint;

    T gcd(T a, T b)
    {
        static if (isFloatingPoint!T)
        {
            return fgcd(a, b);
        }
        else
        {
            import std.numeric : igcd = gcd;
            return igcd(a, b);
        }
    }

    static if (isFloatingPoint!T)
    {
        import std.math : nextUp, nextDown, pow, abs, isFinite;
        import std.algorithm : max;

        T fgcd(T a, T b)
        in
        {
            assert (a.isFinite);
            assert (b.isFinite);
            assert (a < ulong.max);
            assert (b < ulong.max);
        }
        body
        {
            short a_exponent;
int a_digitCount = errol0CountOnly(abs(a), a_exponent);

            short b_exponent;
int b_digitCount = errol0CountOnly(abs(b), b_exponent);

            a_digitCount -= a_exponent;
            if (a_digitCount < 0)
            {
                a_digitCount = 0;
            }

            b_digitCount -= b_exponent;
            if (b_digitCount < 0)
            {
                b_digitCount = 0;
            }

            auto coeff = pow(10, max(a_digitCount, b_digitCount));
            assert (a * coeff < ulong.max);
            assert (b * coeff < ulong.max);
            return (cast(T) euclid(cast(ulong) (a * coeff),
cast(ulong) (b * coeff))) / coeff;
        }

        ulong euclid(ulong a, ulong b)
        {
            while (b != 0)
            {
                auto t = b;
                b = a % b;
                a = t;
            }
            return a;
        }

        struct HighPrecisionFloatingPoint
        {
            T base, offset;

            void normalize()
            {
                T base = this.base;

                this.base   += this.offset;
                this.offset += base - this.base;
            }

            void mul10()
            {
                T base = this.base;

                this.base   *= T(10);
                this.offset *= T(10);

                T offset = this.base;
                offset -= base * T(8);
                offset -= base * T(2);

                this.offset -= offset;

                normalize();
            }

            void div10()
            {
                T base = this.base;

                this.base   /= T(10);
                this.offset /= T(10);

                base -= this.base * T(8);
                base -= this.base * T(2);

                this.offset += base / T(10);

                normalize();
            }
        }
        alias HP = HighPrecisionFloatingPoint;

        enum epsilon = T(0.0000001);
        ushort errol0CountOnly(T f, out short exponent)
        {
            ushort digitCount;

            T ten = T(1);
            exponent = 1;

            auto mid = HP(f, T(0));

while (((mid.base > T(10)) || ((mid.base == T(10)) && (mid.offset >= T(0)))) && (exponent < 308))
            {
                exponent += 1;
                mid.div10();
                ten /= T(10);
            }

while (((mid.base < T(1)) || ((mid.base == T(1)) && (mid.offset < T(0)))) && (exponent > -307))
            {
                exponent -= 1;
                mid.mul10();
                ten *= T(10);
            }

auto inhi = HP(mid.base, mid.offset + (nextUp(f) - f) * ten / (T(2) + epsilon)); auto inlo = HP(mid.base, mid.offset + (nextDown(f) - f) * ten / (T(2) + epsilon));

            inhi.normalize();
            inlo.normalize();

while (inhi.base > T(10) || (inhi.base == T(10) && (inhi.offset >= T(0))))
            {
                exponent += 1;
                inhi.div10();
                inlo.div10();
            }

while (inhi.base < T(1) || (inhi.base == T(1) && (inhi.offset < T(0))))
            {
                exponent -= 1;
                inhi.mul10();
                inlo.mul10();
            }

            while (inhi.base != T(0) || inhi.offset != T(0))
            {
                auto hdig = cast(ubyte) inhi.base;
                if ((inhi.base == hdig) && (inhi.offset < T(0)))
                {
                    hdig -= 1;
                }

                auto ldig = cast(ubyte) inlo.base;
                if ((inlo.base == ldig) && (inlo.offset < 0))
                {
                    ldig -= 1;
                }

                if (ldig != hdig)
                {
                    break;
                }

                digitCount += 1;
                inhi.base -= hdig;
                inhi.mul10();

                inlo.base -= ldig;
                inlo.mul10();
            }

            digitCount += 1;
            return digitCount;
        }
    }
}
---

[1] https://github.com/marcandrysco/Errol

Reply via email to