# Unexpected behaviour with integer sqrt and integer ^

Motivation:
I stumbled upon

√(16)^16 # 0.0


and

√(17)^17 # DomainError with -2.8632214305930583e18:


The reason: This is of course due to integer arithmetic. On my machine:

16^16 # 0
17^17 # -2863221430593058543


Why I think this is unexpected behaviour
I, and I guess most programmers, read √(16)^16 as:

1. call the method √ with argument 16
2. raise the output to the power 16.

This would indeed be the case if I would have used

sqrt(16)^16 # 4.294967296e9


On the other hand, I can imagine, that Julia replaces √(x) by x^(0.5) under the hood. However, ^ acts right associative, so then I would expect something along the line

√(16)^16 == 16^0.5^16 == 16^8.0 == 4.294967296e9


In short, I would not have expected integer arithmetic to matter for √(16)^16, as I expected √(16) naturally promotes to Float64 and the power operation happens after this.

Relevance and next steps?
I found this in a loop:

for M in 1:20
#...
det = √(M)^M
end


i.e., in a setting where one naturally works with integers, so I guess this can be relevant. The fixes are of course obvious: sqrt(M)^M or √(1.0 * M)^M.

I tested this both on Julia 1.6.3 and 1.7.2. on a MacBook Pro 2017.

Is this indeed unexpected behaviour and so far unknown? In that case, should I open an Issue on Github?

1 Like

The problem seems to be that that √ is a prefix operator with lower precedence than ^, resulting in sqrt(16^16) being the code that is run. The strange thing is that looking at the precedences I would interpret it as √ having higher precedence since it is defined on line 99 compared to ^ which is on line 30?

julia> dump(:(√(16)^16))
Expr
args: Array{Any}((2,))
1: Symbol √
2: Expr
args: Array{Any}((3,))
1: Symbol ^
2: Int64 16
3: Int64 16

julia> √16^16 # Parenthesis doesn't matter here since √ is parsed as an operator
0.0

julia> (√16)^16 # This forces sqrt to be run first
4.294967296e9

2 Likes

sqrt and the sqrt symbol are the same function

However operator precedence in the parser might be the problem:

julia> Base.operator_precedence(:^)
15

julia> Base.operator_precedence(:√)
0



^ is one of the operator with highest priority.

3 Likes

No, the documentation clearly states that ^ has higher precedence:

https://docs.julialang.org/en/v1/manual/mathematical-operations/#Operator-Precedence-and-Associativity

2 Likes

julia> Base.operator_precedence(:^)
15
julia> Base.operator_precedence(:√)
0

I don’t think this is representative, see:

julia> Base.operator_precedence(:*)
12
julia> √(4)*4
8.0

i.e., √ has to have a higher precedence as 0.

1 Like
help?> Base.operator_precedence
operator_precedence(s::Symbol)

Return an integer representing the precedence of operator s, relative to other operators. Higher-numbered operators take precedence over lower-numbered operators. Return 0 if s is not a valid operator.


yes 0 means invalid operator. I should have read the documentation. Then I do not know about how the parser handles the precedence of \sqrt wrt to * relative to \sqrt wrt to ^

1 Like

I would guess that √ was just another name for sqrt, this is, it is just a function name and not an operator but apparently it can be used without parenthesis, so it does not seem to be the case. The documentation uses the term “prefix operator”.

"√" can be typed by \sqrt<tab>

search: √

sqrt(x)

Return \sqrt{x}. Throws DomainError for negative Real arguments. Use complex
negative arguments instead. The prefix operator √ is equivalent to sqrt.


1 Like

Nice! Went directly to source to try to find it but reasonable that there is good docs for it

My confusion was referring to the comment on the first line in the linked file which stated ;; Operator precedence table, lowest at top which I interpreted as lower precedence at top of file. Not sure how this is supposed to be interpreted, but probably not as I did at least…

1 Like

Okay, it seem it is now clear what is happening… so my question is: does it make sense like that

Idk, but I feel like √ should have higher precedence than ^ after observing the effects of the converse. Also √(16) feels more like a function call than applying a right associative operator. I quickly scanned parts of the documentation and so far I exclusively found examples with the √(x) syntax instead of √x, so the standard seems to support the “method call” interpretation.

Are there other opinions?

I believe that because √ is a prefix operator, it should be used as √x not √(x). For example, Meta.parse("√x") == Meta.parse("√(x)") == :(√x). If the documentation uses it otherwise, it should be changed.

Could you point us to specific examples? I found 71 occurrences of √ in the JuliaLang/Julia codebase (including /doc) only three of which use parentheses:

julia> .√(1:4)
julia> √(a^2 + a^2) # a^2 overflows
“compositions of unary operators are parsed with right-associativity, e. g., √√-a as √(√(-a)).”

1 Like

Guess lucky-me directly found the few examples of √(x). To be fair, they are on the mathematics page, so it was an obvious place to search for