# Why aren't expressions such as \`isinteger\` simplified in case that the result is inferrable from the arguments?

**URL:** <https://discourse.julialang.org/t/why-arent-expressions-such-as-isinteger-simplified-in-case-that-the-result-is-inferrable-from-the-arguments/91297>\
**Category:** Internals & Design\
**Tags:** constant-propagation\
**Created:** [December 6, 2022, 11:30am UTC](https://discourse.julialang.org/t/why-arent-expressions-such-as-isinteger-simplified-in-case-that-the-result-is-inferrable-from-the-arguments/91297 "2022-12-06T11:30:28Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![jishnub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jishnub/32/33620_2.png) [@jishnub](https://discourse.julialang.org/u/jishnub)\
**Post date:** [December 6, 2022, 11:30am UTC](https://discourse.julialang.org/t/why-arent-expressions-such-as-isinteger-simplified-in-case-that-the-result-is-inferrable-from-the-arguments/91297/1 "2022-12-06T11:30:28Z")

</div>

```julia
julia> @code_typed (n -> isinteger(n+0.5))(2)
CodeInfo(
1 ─ %1 = Base.sitofp(Float64, n)::Float64
│ %2 = Base.add_float(%1, 0.5)::Float64
│ %3 = Base.trunc_llvm(%2)::Float64
│ %4 = Base.sub_float(%2, %3)::Float64
│ %5 = Base.eq_float(%4, 0.0)::Bool
│ %6 = Base.and_int(%5, true)::Bool
│ %7 = Base.and_int(%6, true)::Bool
└── return %7
) => Bool

```

Given that `n` is an integer, and the constant `0.5`, it should be possible to infer that the `n+0.5` isn’t an integer. I wonder this simplification isn’t carried out here, although the compiler does propagate constants if `n` is a known at compile-time as well?

```julia
julia> @code_typed (() -> (n = 1; isinteger(n+0.5)))()
CodeInfo(
1 ─ return false
) => Bool

```

---

<div class="post-metadata">

**Author:** ![jishnub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jishnub/32/33620_2.png) [@jishnub](https://discourse.julialang.org/u/jishnub)\
**Post date:** [December 6, 2022, 11:31am UTC](https://discourse.julialang.org/t/why-arent-expressions-such-as-isinteger-simplified-in-case-that-the-result-is-inferrable-from-the-arguments/91297/2 "2022-12-06T11:31:38Z")

</div>

Ah well,

```julia
julia> isinteger(typemax(Int) + 0.5)
true

```

Although,

```julia
julia> isinteger(typemax(Int32) + 0.5)
false

julia> isinteger(typemin(Int32) + 0.5)
false

```

but this is still not inferred

```julia
julia> @code_typed (n -> isinteger(n+0.5))(Int32(1))
CodeInfo(
1 ─ %1 = Base.sitofp(Float64, n)::Float64
│ %2 = Base.add_float(%1, 0.5)::Float64
│ %3 = Base.trunc_llvm(%2)::Float64
│ %4 = Base.sub_float(%2, %3)::Float64
│ %5 = Base.eq_float(%4, 0.0)::Bool
│ %6 = Base.and_int(%5, true)::Bool
│ %7 = Base.and_int(%6, true)::Bool
└── return %7
) => Bool

```

---

<div class="post-metadata">

**Author:** ![StefanKarpinski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stefankarpinski/32/24_2.png) [@StefanKarpinski](https://discourse.julialang.org/u/StefanKarpinski)\
**Post date:** [December 6, 2022, 7:23pm UTC](https://discourse.julialang.org/t/why-arent-expressions-such-as-isinteger-simplified-in-case-that-the-result-is-inferrable-from-the-arguments/91297/3 "2022-12-06T19:23:45Z")

</div>

There are some corner cases where the value of `isinteger` can be predicted, but it’s a pretty tricky interaction between the `eps(x)` graph for the float type, the range of the integer type, and the specific float constant that’s being added. Let’s say we have this function:

```julia
f(x::AbstractFloat, n::Integer) = isinteger(x + n)

```

and we say `F = typeof(x)` and `T = typeof(n)`. When could we constant fold based on the value of `x`? It’s a pretty complex set of rules that LLVM doesn’t know.

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [December 11, 2022, 4:54am UTC](https://discourse.julialang.org/t/why-arent-expressions-such-as-isinteger-simplified-in-case-that-the-result-is-inferrable-from-the-arguments/91297/4 "2022-12-11T04:54:14Z")

</div>

Can it truly be known that `n+0.5` is not an integer?

```julia
julia> a+b = round(Int, a)*round(Int, b)
+ (generic function with 1 method)

julia> 1.5+2.3
4

julia> isinteger(1.5+2.3)
true

```

😁

---

<div class="post-metadata">

**Author:** ![jishnub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jishnub/32/33620_2.png) [@jishnub](https://discourse.julialang.org/u/jishnub)\
**Post date:** [December 11, 2022, 5:48am UTC](https://discourse.julialang.org/t/why-arent-expressions-such-as-isinteger-simplified-in-case-that-the-result-is-inferrable-from-the-arguments/91297/5 "2022-12-11T05:48:24Z")

</div>

I’m referring to `Base.+(n, 0.5)` for an integer `n`

---

<div class="post-metadata">

**Author:** ![StefanKarpinski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stefankarpinski/32/24_2.png) [@StefanKarpinski](https://discourse.julialang.org/u/StefanKarpinski)\
**Post date:** [December 11, 2022, 6:33am UTC](https://discourse.julialang.org/t/why-arent-expressions-such-as-isinteger-simplified-in-case-that-the-result-is-inferrable-from-the-arguments/91297/6 "2022-12-11T06:33:17Z")

</div>

Mathematically yea, but it fails when a value is big enough because floats stop being able to represent non-integral values.

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [December 11, 2022, 8:37am UTC](https://discourse.julialang.org/t/why-arent-expressions-such-as-isinteger-simplified-in-case-that-the-result-is-inferrable-from-the-arguments/91297/7 "2022-12-11T08:37:20Z")

</div>

🤔

```julia
julia> Base.:+(a::Int, b::Float64) = a+round(Int, b)

julia> n=2
2

julia> isinteger(n+0.5)
true

```

---

<div class="post-metadata">

**Author:** ![jishnub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jishnub/32/33620_2.png) [@jishnub](https://discourse.julialang.org/u/jishnub)\
**Post date:** [December 11, 2022, 9:36am UTC](https://discourse.julialang.org/t/why-arent-expressions-such-as-isinteger-simplified-in-case-that-the-result-is-inferrable-from-the-arguments/91297/8 "2022-12-11T09:36:06Z")

</div>

This is type-piracy, which I’m assuming isn’t happening. In any case, the point not about methods being overwritten, it’s about the method as defined in Base not being able to infer the result. I’m not talking about a general case where I don’t know the method that’s called, given the arguments.

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [December 11, 2022, 7:59pm UTC](https://discourse.julialang.org/t/why-arent-expressions-such-as-isinteger-simplified-in-case-that-the-result-is-inferrable-from-the-arguments/91297/9 "2022-12-11T19:59:48Z")

</div>

Sorry, you’re right; even with my type piracy, the types are inferred and constants propagated. My input unfortunately degraded the conversation’s SNR.

```julia
julia> @code_typed (() -> (n = 1; isinteger(n+0.5)))()
CodeInfo(
1 ─ return false
) => Bool

julia> Base.:+(a::Int, b::Float64) = a+round(Int, b)

julia> @code_typed (() -> (n = 1; isinteger(n+0.5)))()
CodeInfo(
1 ─ return true
) => Bool

```

Whereas when `n` is an argument to the function, rather than being locally declared (fresh Julia process):

```julia
julia> @code_typed ((n::Int) -> (isinteger(n::Int+0.5)))(1::Int)
CodeInfo(
1 ─ %1 = Base.sitofp(Float64, n)::Float64
│ %2 = Base.add_float(%1, 0.5)::Float64
│ %3 = Base.trunc_llvm(%2)::Float64
│ %4 = Base.sub_float(%2, %3)::Float64
│ %5 = Base.eq_float(%4, 0.0)::Bool
│ %6 = Base.and_int(%5, true)::Bool
│ %7 = Base.and_int(%6, true)::Bool
└── return %7
) => Bool

julia> Base.:+(a::Int, b::Float64) = a+round(Int, b)

julia> @code_typed ((n) -> (isinteger(n+0.5)))(1)
CodeInfo(
1 ─ return true
) => Bool

```

even with all the type annotations, it doesn’t work the same because

> [@StefanKarpinski](#):
>
> it’s a pretty tricky interaction between the `eps(x)` graph for the float type, the range of the integer type, and the specific float constant that’s being added.
