gcd with doubles
Moritz Maxeiner via Digitalmars-d-learn
digitalmars-d-learn at puremagic.com
Sun Aug 27 14:25:47 PDT 2017
On Sunday, 27 August 2017 at 19:47:59 UTC, Alex wrote:
> Hi, all.
> Can anybody explain to me why
>
> void main()
> {
> import std.numeric;
> assert(gcd(0.5,32) == 0.5);
> assert(gcd(0.2,32) == 0.2);
> }
>
> fails on the second assert?
>
> I'm aware, that calculating gcd on doubles is not so obvios, as
> on integers. But if the library accepts doubles, and basically
> the return is correct occasionally, why it is not always the
> case?
If the type isn't a builtin integral and can't be bit shifted,
the gcd algorithm falls back to using the Euclidean algorithm in
order to support custom number types, so the above gdc in the
above reduces to:
---
double gcd(double a, double b)
{
while (b != 0)
{
auto t = b;
b = a % b;
a = t;
}
return a;
}
---
The issue boils down to the fact that `32 % 0.2` yield `0.2`
instead of `0.0`, so the best answer I can give is "because
floating points calculations are approximations". I'm actually
not sure if this is a bug in fmod or expected behaviour, but I'd
tend to the latter.
> Is there a workaround, maybe?
If you know how many digits of precision after the decimal dot
you can multiply beforehand, gcd in integer realm, and div
afterwards (be warned, the below is only an example
implementation for readability, it does not do the required
overflow checks for the double -> ulong conversion!):
---
import std.traits : isFloatingPoint;
T gcd(ubyte precision, T)(T a, T b) if (isFloatingPoint!T)
{
import std.numeric : _gcd = gcd;
immutable T coeff = 10 * precision;
return (cast(T) _gcd(cast(ulong) (a * coeff),
cast(ulong) (b * coeff))) / coeff;
}
---
More information about the Digitalmars-d-learn
mailing list