Skip to content

Commit 7db53a5

Browse files
authored
Fix gcd for floating point numbers (#185)
1 parent 18090b9 commit 7db53a5

File tree

3 files changed

+37
-6
lines changed

3 files changed

+37
-6
lines changed

src/division.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,13 @@ function _div(f::APL, g::APL)
5656
q = zero(rf)
5757
while !iszero(rf)
5858
ltf = leadingterm(rf)
59+
if !divides(lt, ltf)
60+
# In floating point arithmetics, it may happen
61+
# that `rf` is not zero even if it cannot be reduced further.
62+
# As `_div` assumes that `g` divides `f`, we know that
63+
# `rf` is approximately zero anyway.
64+
break
65+
end
5966
qt = _div(ltf, lt)
6067
q = MA.add!!(q, qt)
6168
rf = MA.operate!!(removeleadingterm, rf)

src/gcd.jl

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,14 @@ Addison-Wesley Professional. Third edition.
4545
"""
4646
struct SubresultantAlgorithm <: AbstractUnivariateGCDAlgorithm end
4747

48+
_coefficient_gcd(α, β) = gcd(α, β)
49+
_coefficient_gcd::AbstractFloat, β) = one(Base.promote_typeof(α, β))
50+
_coefficient_gcd(α, β::AbstractFloat) = one(Base.promote_typeof(α, β))
51+
_coefficient_gcd::AbstractFloat, β::AbstractFloat) = one(Base.promote_typeof(α, β))
52+
4853
Base.lcm(p::APL, q::APL, algo::AbstractUnivariateGCDAlgorithm=GeneralizedEuclideanAlgorithm()) = p * div(q, gcd(p, q, algo))
49-
Base.gcd(α, p::APL, algo::AbstractUnivariateGCDAlgorithm=GeneralizedEuclideanAlgorithm()) = gcd(α, content(p, algo))
50-
Base.gcd(p::APL, α, algo::AbstractUnivariateGCDAlgorithm=GeneralizedEuclideanAlgorithm()) = gcd(content(p, algo), α)
54+
Base.gcd(α, p::APL, algo::AbstractUnivariateGCDAlgorithm=GeneralizedEuclideanAlgorithm()) = _coefficient_gcd(α, content(p, algo))
55+
Base.gcd(p::APL, α, algo::AbstractUnivariateGCDAlgorithm=GeneralizedEuclideanAlgorithm()) = _coefficient_gcd(content(p, algo), α)
5156

5257
#function MA.promote_operation(::typeof(gcd), P::Type{<:Number}, Q::Type{<:Number})
5358
# return typeof(gcd(one(P), one(Q)))
@@ -525,6 +530,9 @@ function content(poly::APL{T}, algo::AbstractUnivariateGCDAlgorithm) where {T}
525530
init = zero(P),
526531
)::P
527532
end
533+
function content(poly::APL{T}, algo::AbstractUnivariateGCDAlgorithm) where {T<:AbstractFloat}
534+
return one(T)
535+
end
528536

529537
"""
530538
primitive_part_content(poly::AbstractPolynomialLike{T}, algo::AbstractUnivariateGCDAlgorithm) where {T}

test/division.jl

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,17 +139,31 @@ function univariate_gcd_test(algo=GeneralizedEuclideanAlgorithm())
139139
end
140140

141141
function _mult_test(a::Number, b)
142-
return @test iszero(maxdegree(b))
142+
@test iszero(maxdegree(b))
143+
end
144+
function _mult_test(a::Number, b::Number)
145+
@test iszero(rem(a, b))
146+
@test iszero(rem(b, a))
143147
end
144148
function _mult_test(a, b)
145149
@test iszero(rem(a, b))
146150
@test iszero(rem(b, a))
147151
end
148152
function mult_test(expected, a, b, algo)
149-
g = @inferred gcd(a, b, algo)
153+
g = @inferred MP._simplifier(a, b, algo)
150154
@test g isa promote_type(polynomialtype(a), polynomialtype(b))
151155
_mult_test(expected, g)
152156
end
157+
function mult_test(expected, a::Number, b, algo)
158+
g = @inferred MP._simplifier(a, b, algo)
159+
@test g isa promote_type(typeof(a), MP.coefficienttype(b))
160+
_mult_test(expected, g)
161+
end
162+
function mult_test(expected, a, b::Number, algo)
163+
g = @inferred MP._simplifier(a, b, algo)
164+
@test g isa promote_type(MP.coefficienttype(a), typeof(b))
165+
_mult_test(expected, g)
166+
end
153167
function sym_test(a, b, g, algo)
154168
mult_test(g, a, b, algo)
155169
mult_test(g, b, a, algo)
@@ -164,6 +178,7 @@ function multivariate_gcd_test(::Type{T}, algo=GeneralizedEuclideanAlgorithm())
164178
Mod.@polyvar x y z
165179
o = one(T)
166180
zr = zero(T)
181+
sym_test(o, o * x, o, algo)
167182
sym_test(o * x, zr * x, o * x, algo)
168183
sym_test(o * x + o, zr * x, o * x + o, algo)
169184
# Inspired from https://github.com/JuliaAlgebra/MultivariatePolynomials.jl/issues/160
@@ -224,7 +239,8 @@ function multivariate_gcd_test(::Type{T}, algo=GeneralizedEuclideanAlgorithm())
224239
end
225240
sym_test(b, c, x + y + z, algo)
226241
sym_test(c, a, z^3 + y^2 + x, algo)
227-
if T != Int || (algo != GeneralizedEuclideanAlgorithm(false, false) && algo != GeneralizedEuclideanAlgorithm(true, false) && algo != GeneralizedEuclideanAlgorithm(false, true))
242+
if (T != Int || (algo != GeneralizedEuclideanAlgorithm(false, false) && algo != GeneralizedEuclideanAlgorithm(true, false) && algo != GeneralizedEuclideanAlgorithm(false, true))) &&
243+
(T != Float64 || (algo != GeneralizedEuclideanAlgorithm(false, true) && algo != GeneralizedEuclideanAlgorithm(true, true)))
228244
triple_test(a, b, c, algo)
229245
end
230246
end
@@ -312,7 +328,7 @@ end
312328
univariate_gcd_test(GeneralizedEuclideanAlgorithm(primitive_rem, skip_last))
313329
end
314330
end
315-
@testset "Multivariate gcd $T" for T in [Int, BigInt, Rational{BigInt}]
331+
@testset "Multivariate gcd $T" for T in [Int, BigInt, Rational{BigInt}, Float64]
316332
if T != Rational{BigInt} || VERSION >= v"1.6"
317333
# `gcd` for `Rational{BigInt}` got defined at some point between v1.0 and v1.6
318334
@testset "primitive_rem=$primitive_rem" for primitive_rem in [false, true]

0 commit comments

Comments
 (0)