# Differences in collecting algebraic terms or not

**URL:** https://discourse.julialang.org/t/differences-in-collecting-algebraic-terms-or-not/32488
**Category:** Performance
**Tags:** question
**Created:** [December 19, 2019, 5:44pm UTC](https://discourse.julialang.org/t/differences-in-collecting-algebraic-terms-or-not/32488 "2019-12-19T17:44:31Z")
**Posts on this page:** 6
**Page:** 1

<div class="post-metadata">

### Author: ![Donkeyfish87](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/donkeyfish87/32/10654_2.png) [@Donkeyfish87](https://discourse.julialang.org/u/Donkeyfish87)
#### Post date: [December 19, 2019, 5:44pm UTC](https://discourse.julialang.org/t/differences-in-collecting-algebraic-terms-or-not/32488/1 "2019-12-19T17:44:31Z")

</div>

Why is there a difference between

```julia
function sum_of_squares(n::Int)
    div(n*(n+1)*(2n+1), 6)
end

function square_of_sum(n::Int)
    div(n*(n+1),2)^2
end

function difference(n::Int)
    square_of_sum(n) - sum_of_squares(n)
end

```

And

```julia
function difference2(n::Int)
    (1.5n^4 + n^3 - 1.5n^2 - n) / 6
end

```

When I run

```julia
difference(2)
difference2(2)

const num = 10_000
@benchmark difference(num)
@benchmark difference2(num)

```

Not like there’s a massive gap, but I don’t understand the reason. Perhaps one of you could help me out.

---

<div class="post-metadata">

### Author: ![orialb](https://avatars.discourse-cdn.com/v4/letter/o/65b543/32.png) [@orialb](https://discourse.julialang.org/u/orialb)
#### Post date: [December 19, 2019, 6:14pm UTC](https://discourse.julialang.org/t/differences-in-collecting-algebraic-terms-or-not/32488/2 "2019-12-19T18:14:35Z")

</div>

Not directly related to performance, but it seems that the two functions don’t compute the same thing, I get

```julia
difference2(10_000) - difference(10_000) # = 3333.5

```

I guess you have some typo somewhere?

---

<div class="post-metadata">

### Author: ![Donkeyfish87](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/donkeyfish87/32/10654_2.png) [@Donkeyfish87](https://discourse.julialang.org/u/Donkeyfish87)
#### Post date: [December 19, 2019, 6:34pm UTC](https://discourse.julialang.org/t/differences-in-collecting-algebraic-terms-or-not/32488/3 "2019-12-19T18:34:10Z")

</div>

Ops you’re right, that’s a bit embarrassing. It’s supposed to be `-n` in the difference2 function.

I still don’t get the same bm outputs though. Edited now

---

<div class="post-metadata">

### Author: ![dlakelan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlakelan/32/8491_2.png) [@dlakelan](https://discourse.julialang.org/u/dlakelan)
#### Post date: [December 19, 2019, 7:00pm UTC](https://discourse.julialang.org/t/differences-in-collecting-algebraic-terms-or-not/32488/4 "2019-12-19T19:00:28Z")

</div>

> [@Donkeyfish87](#):
>
> Not like there’s a massive gap, but I don’t understand the reason. Perhaps one of you could help me out.

difference2 uses floating point coefficients, and so the answer is converted to a floating point number. Whereas difference uses entirely integer coefficients everywhere and its calculated as an exact integer.

---

<div class="post-metadata">

### Author: ![orialb](https://avatars.discourse-cdn.com/v4/letter/o/65b543/32.png) [@orialb](https://discourse.julialang.org/u/orialb)
#### Post date: [December 19, 2019, 7:05pm UTC](https://discourse.julialang.org/t/differences-in-collecting-algebraic-terms-or-not/32488/5 "2019-12-19T19:05:41Z")

</div>

As a side remark I also noticed that the timings in your benchmark are quite small

```julia
julia> @btime difference(10_000)
  0.026 ns (0 allocations: 0 bytes)
2500166641665000

julia> @btime difference2(10_000)
  1.496 ns (0 allocations: 0 bytes)
2.500166641665e15

```

As I recently learned from the [excellent notes](https://mitmath.github.io/18337/lecture2/optimizing) by Chris Rackauckas, this might mean that the compiler is doing constant propagation and basically replacing the function call with the result and not doing the computation at all.  
Using the same trick as from “Note on benchmarking” section of the notes we can see that this is indeed the case, as far as I understand (true for both functions):

```julia
julia> cheat() = difference(10_000)
cheat (generic function with 1 method)

julia> @code_llvm cheat()

; @ REPL[8]:1 within `cheat'
define i64 @julia_cheat_12467() {
top:
  ret i64 2500166641665000
}

```

Just to further exemplify this (again I think this is what is happening, I’m not an expert):

```julia
julia> x = 100;

julia> @btime difference(x);
  6.789 ns (0 allocations: 0 bytes)

julia> @btime difference2(x);
  8.567 ns (0 allocations: 0 bytes)

julia> @btime difference(100);
  0.026 ns (0 allocations: 0 bytes)

```

But as already written while I was writing this answer, seems that the extra time difference is due to the floats vs. ints in the two functions.

---

<div class="post-metadata">

### Author: ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)
#### Post date: [December 19, 2019, 9:34pm UTC](https://discourse.julialang.org/t/differences-in-collecting-algebraic-terms-or-not/32488/6 "2019-12-19T21:34:11Z")

</div>

Floating point calculations on integers and integers multiplied by powers of 2 (e.g. `1.5`) are actually _exact_ until you exceed the maximum number of digits that are stored (52 bits), at which point their accuracy will degrade gracefully (making some errors in the least-significant bits). That happens around `n=7403` for this calculation.

In contrast, working with `Int` means that you will get catastrophic failure once you overflow 64 bits, which happens around `n == 80000` for this calculation:

```julia
julia> square_of_sum(80000)
-8206488072109551616

julia> difference(80000)
-8206658741976231616

```

Broadening your signatures from `::Int` to `::Integer`, so that they accept any integer type, we can still get the exact answer (at considerably more expense) with `BigInt`:

```julia
julia> difference(big(80000))
10240085331733320000

```

but doing this calculation in floating-point with `difference(n::AbstractFloat) = (1.5n^4 + n^3 - 1.5n^2 - n) / 6` gives almost the right answer (to ≈16 significant digits) much more quickly:

```julia
julia> difference(80000.0)
1.024008533173332e19

julia> (difference(80000.0) - difference(big(80000))) / difference(big(80000))
-3.124973959038622594888003211964321226280055103023663464806989420192726958281252e-17

```
