Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions math/lcm/lcm.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,33 @@
package lcm

import (
"math"

"github.com/TheAlgorithms/Go/math/gcd"
)

// absInt64 returns the absolute value of x as an int64.
func absInt64(x int64) int64 {
if x < 0 {
return -x
}
return x
}

// Lcm returns the lcm of two numbers using the fact that lcm(a,b) * gcd(a,b) = | a * b |
//
// The computation is performed with integer arithmetic and the division by gcd
// is applied to |a| before multiplying by |b|, so the running intermediate never
// exceeds the true LCM. This avoids two failure modes of the previous
// float64-based formula:
// - int64 multiply overflow: a*b wraps mod 2^64 before the float64 cast, so
// e.g. Lcm(4294967296, 8589934592) returned 0 (impossible for positives).
// - float64 precision loss: float64 cannot represent every int64 past 2^53,
// so e.g. Lcm(9007199254740993, 1) returned 9007199254740992, violating the
// identity lcm(x, 1) == x.
func Lcm(a, b int64) int64 {
return int64(math.Abs(float64(a*b)) / float64(gcd.Iterative(a, b)))
g := gcd.Iterative(a, b)
if g == 0 {
// gcd.Iterative(0, 0) == 0; LCM(0, x) is conventionally 0.
return 0
}
return (absInt64(a) / absInt64(g)) * absInt64(b)
}
29 changes: 29 additions & 0 deletions math/lcm/lcm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,35 @@ func TestLcm(t *testing.T) {
b: 5,
output: 5,
},
// Regression: integer-arithmetic fix for float precision loss and overflow.
{
// Mode A: |a*b| overflows int64 even though the true LCM fits.
// Previously returned 0 (impossible for positive inputs).
name: "LCM of 2^32 & 2^33 (overflow-safe intermediate)",
a: 4294967296,
b: 8589934592,
output: 8589934592,
}, {
// Mode A: lcm(x, x) == x. Previously lcm(x,x) != x for large x.
name: "LCM of 3037000500 & 3037000500 (lcm(x,x)==x)",
a: 3037000500,
b: 3037000500,
output: 3037000500,
}, {
// Mode B: float64 cannot represent 2^53+1 exactly.
// Previously returned 9007199254740992, violating lcm(x,1)==x.
name: "LCM of 9007199254740993 & 1 (2^53+1 precision)",
a: 9007199254740993,
b: 1,
output: 9007199254740993,
}, {
// Mode B: product exceeds 2^53 but fits int64; float64 loses low bits.
// Previously returned 1000000016000000000 (off by 63).
name: "LCM of 1000000007 & 1000000009 (large coprime precision)",
a: 1000000007,
b: 1000000009,
output: 1000000016000000063,
},
}

for _, tc := range testCases {
Expand Down