# Fast check if a character "is" an integer

**URL:** https://discourse.julialang.org/t/fast-check-if-a-character-is-an-integer/88750
**Category:** Performance
**Created:** [October 14, 2022, 7:20pm UTC](https://discourse.julialang.org/t/fast-check-if-a-character-is-an-integer/88750 "2022-10-14T19:20:40Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)
#### Post date: [October 14, 2022, 7:20pm UTC](https://discourse.julialang.org/t/fast-check-if-a-character-is-an-integer/88750/1 "2022-10-14T19:20:41Z")

</div>

I need to check if the first character of a string is an integer, quickly. I’m doing a try-catch:

```julia
julia> function c(name)
           i0 = try 
                parse(Int, name[1])
                true
           catch
                false
           end
           return i0
       end
c (generic function with 1 method)

julia> @btime c("1ABC")
  20.250 ns (0 allocations: 0 bytes)
true

julia> @btime c("ABC")
  230.796 μs (9 allocations: 440 bytes)
false

```

but this is painfully slow when the try fails. Is there a better way?

---

<div class="post-metadata">

### Author: ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)
#### Post date: [October 14, 2022, 7:25pm UTC](https://discourse.julialang.org/t/fast-check-if-a-character-is-an-integer/88750/2 "2022-10-14T19:25:24Z")

</div>

Can’t you do something like

```julia
Int('0') <= first(name) <= Int('9')

```

?

Not sure if `Int` conversion is necessary.

Edit: Okay, I’m messing up something. But compare numerical value of characters.

---

<div class="post-metadata">

### Author: ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)
#### Post date: [October 14, 2022, 7:26pm UTC](https://discourse.julialang.org/t/fast-check-if-a-character-is-an-integer/88750/3 "2022-10-14T19:26:16Z")

</div>

`isdigit(first("1ABC"))`

---

<div class="post-metadata">

### Author: ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)
#### Post date: [October 14, 2022, 7:27pm UTC](https://discourse.julialang.org/t/fast-check-if-a-character-is-an-integer/88750/4 "2022-10-14T19:27:26Z")

</div>

Maybe you are looking for `tryparse`. Exactly like `parse` but returns nothing if not Int.

```julia
c(name) = isnothing(tryparse(Int, name[1:1]))

```

---

<div class="post-metadata">

### Author: ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)
#### Post date: [October 14, 2022, 7:28pm UTC](https://discourse.julialang.org/t/fast-check-if-a-character-is-an-integer/88750/5 "2022-10-14T19:28:01Z")

</div>

Thanks @rafael.guerra . That is quick.

---

<div class="post-metadata">

### Author: ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)
#### Post date: [October 14, 2022, 7:29pm UTC](https://discourse.julialang.org/t/fast-check-if-a-character-is-an-integer/88750/6 "2022-10-14T19:29:41Z")

</div>

`isdigit` is The Way. I just want to say that it does the same thing as my suggestion 😁

---

<div class="post-metadata">

### Author: ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)
#### Post date: [October 14, 2022, 7:36pm UTC](https://discourse.julialang.org/t/fast-check-if-a-character-is-an-integer/88750/7 "2022-10-14T19:36:17Z")

</div>

In any case, this question raised an issue:

```julia
julia> parse(Int, '1')
1

```

while

```julia
julia> tryparse(Int, '1')
ERROR: MethodError: no method matching tryparse(::Type{Int64}, ::Char)
Closest candidates are:
  tryparse(::Type{T}, ::AbstractString; base) where T<:Integer at parse.jl:235
Stacktrace:
 [1] top-level scope
   @ REPL[221]:1

```

This should get a minor PR. Anyone else has thoughts?

---

<div class="post-metadata">

### Author: ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)
#### Post date: [October 14, 2022, 7:41pm UTC](https://discourse.julialang.org/t/fast-check-if-a-character-is-an-integer/88750/9 "2022-10-14T19:41:00Z")

</div>

Of course, problem is difference between `parse` and `tryparse`

---

<div class="post-metadata">

### Author: ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)
#### Post date: [October 14, 2022, 7:43pm UTC](https://discourse.julialang.org/t/fast-check-if-a-character-is-an-integer/88750/10 "2022-10-14T19:43:29Z")

</div>

Check [this issue](https://github.com/JuliaLang/julia/issues/45640).

---

<div class="post-metadata">

### Author: ![Palli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/palli/32/3380_2.png) [@Palli](https://discourse.julialang.org/u/Palli)
#### Post date: [October 14, 2022, 7:49pm UTC](https://discourse.julialang.org/t/fast-check-if-a-character-is-an-integer/88750/11 "2022-10-14T19:49:54Z")

</div>

Except it isn’t. Yours is slower, though both should be pretty fast…

---

<div class="post-metadata">

### Author: ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)
#### Post date: [October 14, 2022, 7:59pm UTC](https://discourse.julialang.org/t/fast-check-if-a-character-is-an-integer/88750/12 "2022-10-14T19:59:05Z")

</div>

DNF’s proposal seems to me as quick, dropping the `Int` part:  
`'0' <= first(str) <= '9'`

---

<div class="post-metadata">

### Author: ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)
#### Post date: [October 14, 2022, 8:01pm UTC](https://discourse.julialang.org/t/fast-check-if-a-character-is-an-integer/88750/13 "2022-10-14T20:01:23Z")

</div>

I don’t have my computer, so I was just ‘waving in the right direction’.

---

<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: [October 14, 2022, 11:32pm UTC](https://discourse.julialang.org/t/fast-check-if-a-character-is-an-integer/88750/14 "2022-10-14T23:32:01Z")

</div>

> [@rafael.guerra](#):
>
> `isdigit(first("1ABC"))`

This is definitely faster than the original method using `try`/`catch` with `parse`, and is clear.

_However_, if we’re playing the benchmarks game here and you want the fastest possible thing, I would avoid even the call to `first(name)`, which requires extracting a whole Unicode character. Instead, at least for `String`, you could check only the first _byte_ of the string, e.g.

```julia
# stupid optimization tricks:
c(name::Union{String,SubString{String}}) =
    UInt8('0') ≤ codeunit(name, 1) ≤ UInt8('9')

```

(And if this is in a context where you know for _sure_ that the strings are non-empty, you could add `@inbounds`.)

---

<div class="post-metadata">

### Author: ![Palli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/palli/32/3380_2.png) [@Palli](https://discourse.julialang.org/u/Palli)
#### Post date: [October 17, 2022, 10:06pm UTC](https://discourse.julialang.org/t/fast-check-if-a-character-is-an-integer/88750/15 "2022-10-17T22:06:11Z")

</div>

I tried with `@inbounds` on an empty string and I still got BoundsError, so I think it might be safe either way.

Anyway I think we might be spending way too much time optimizing an O(1) function…

But I’m interested in this since I plan to make my own string type. For the current one, is has a pointer to the heap for the string. Can that object be relied to exist, and if empty, at least the first byte be zero (are strings zero terminated?).

The code is much longer that I would have liked, can throw, and I would like false instead for empty strings:

```julia
@code_native @inbounds codeunit(@view(""[1:1]), 1)

```

---

<div class="post-metadata">

### Author: ![Jake](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jake/32/46007_2.png) [@Jake](https://discourse.julialang.org/u/Jake)
#### Post date: [October 17, 2022, 11:02pm UTC](https://discourse.julialang.org/t/fast-check-if-a-character-is-an-integer/88750/16 "2022-10-17T23:02:01Z")

</div>

Some good ideas, if I want to expand the question to check if a string is a positive number , what would I do? I can build on the above to get

```julia
julia> all(isdigit.(collect("ABC123")))
false

julia> a = "123"
"123"

julia> all(isdigit.(collect(a))) && parse(Int, a) >= 0
true

julia> a = "-123"
"-123"

julia> all(isdigit.(collect(a))) && parse(Int, a) >= 0
false

```

Is this the best way to go about it?  
Or Steven’s way?

```julia
julia> a = "123"
"123"

julia> all(UInt8('0') .<= codeunit.(a, 1:length(a)) .<= UInt8('9'))
true

```

---

<div class="post-metadata">

### Author: ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)
#### Post date: [October 18, 2022, 7:52am UTC](https://discourse.julialang.org/t/fast-check-if-a-character-is-an-integer/88750/17 "2022-10-18T07:52:44Z")

</div>

> [@Jake](#):
>
> `all(isdigit.(collect("ABC123")))`

Firstly, the above is quite inefficient. It collects the entire string into a vector of `Char`s, then runs `isdigit` on every single character, creating another temporary array, and then, finally, runs `all` on the output of this.

Instead, avoid collecting anything (in fact, if your code contains `collect` you are probably wasting time):

```julia
all(isdigit, "ABC123")

```

This will check the first character, and immediately bail out with zero allocations. Compare performance here:

```julia
julia> @btime all(isdigit.(collect($str)))
  82.505 ns (3 allocations: 176 bytes)
false

julia> @btime all(isdigit, $str)
  8.500 ns (0 allocations: 0 bytes)
false

```

Key to good performance is to avoid unnecessary intermediate allocations, and in the first example, you have got three of them.

As for the parsing part, I don’t understand why you need that at all. If all characters in a string are digits, then it’s a positive integer, so you only need

```julia
ispositiveinteger(str) = all(isdigit, str)

julia> ispositiveinteger("12345")
true

julia> ispositiveinteger("-12345")
false

```

Or do you need to check for whitespace, underscores, etc?

In fact, `parse(Int, str)` might ruin things, because of potential overflow:

```julia
julia> parse(Int, "9223372036854775808")
ERROR: OverflowError: overflow parsing "9223372036854775808"

julia> ispositiveinteger("9223372036854775808") # this still works
true

```

---

<div class="post-metadata">

### Author: ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)
#### Post date: [October 18, 2022, 8:09am UTC](https://discourse.julialang.org/t/fast-check-if-a-character-is-an-integer/88750/18 "2022-10-18T08:09:40Z")

</div>

> [@Jake](#):
>
> ```julia
> julia> all(UInt8('0') .<= codeunit.(a, 1:length(a)) .<= UInt8('9'))
> 
> ```

The above will be slow, because of the needless allocations. The fastest I can come up with is, using the same `codeunit` trick is

```julia
all(x -> (0x30 <= x <= 0x39), codeunits(str))

```

This has no allocations (`0x39` is the same as `UInt8('0')`, just shorter).

But I’m a bit wary of the whole `codeunits` thing, I never feel sure if there might be some way for some unicode value to trick the code. Does anyone know if this is completely safe? If it is safe, you can just use

```julia
ispositiveinteger(str) = all(x->(0x30 <= x <= 0x39), codeunits(str))

```

On a more general note:  
Broadcasting (and `collect`) produces arrays (with some exceptions, like when using it on a tuple). If what you are trying to calculate is ultimately an array with one or more output elements per input element, then broadcasting is fine, just make sure to fuse the broadcasts (but don’t use `collect`, it is almost always bad.)

If, on the other hand, the value you want to calculate is a _scalar_, if it is a _reduction_ over your input collection, then you should probably avoid broadcasting. Use `sum(foo, vect)`, not `sum(foo.(vect))`; use `all(foo, vect)`, not `all(foo.(vect))`, and so on, for `minimum`, `maximum`, `any`, `count`, etc. etc. And use loops where appropriate.

---

<div class="post-metadata">

### Author: ![cjdoris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cjdoris/32/213133_2.png) [@cjdoris](https://discourse.julialang.org/u/cjdoris)
#### Post date: [October 18, 2022, 10:29am UTC](https://discourse.julialang.org/t/fast-check-if-a-character-is-an-integer/88750/19 "2022-10-18T10:29:30Z")

</div>

> [@DNF](#):
>
> But I’m a bit wary of the whole `codeunits` thing, I never feel sure if there might be some way for some unicode value to trick the code. Does anyone know if this is completely safe?

A nice property of UTF-8 is that any codeunit with the top bit unset (i.e. a codeunit \< 128) is always an ASCII character, regardless of what came before. Hence the above code to check if a string is entirely ASCII digits is perfectly correct.

---

<div class="post-metadata">

### Author: ![Palli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/palli/32/3380_2.png) [@Palli](https://discourse.julialang.org/u/Palli)
#### Post date: [October 18, 2022, 11:01am UTC](https://discourse.julialang.org/t/fast-check-if-a-character-is-an-integer/88750/20 "2022-10-18T11:01:39Z")

</div>

> [@DNF](#):
>
> ```julia
> all(x -> (0x30 <= x <= 0x39), codeunits(str))
> 
> ```
> 
> This has no allocations

That gives true for the empty string, which I do not like, and ~~actually gives me “7.92 k allocations” a lot more than~~ :

`codeunit.(a, 1:length(a))` which is wrong, since the “length” in codeunits can be longer, I think it needs to be `codeunit.(a, eachindex(a[1:end]))`. Is there a better way to do this, and faster, both have allocations, this one more.

---

<div class="post-metadata">

### Author: ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)
#### Post date: [October 18, 2022, 11:58am UTC](https://discourse.julialang.org/t/fast-check-if-a-character-is-an-integer/88750/21 "2022-10-18T11:58:01Z")

</div>

> [@Palli](#):
>
> That gives true for the empty string,

That’s not nice, but:

> [@Palli](#):
>
> and actually gives me “7.92 k allocations”

this I cannot reproduce:

```julia
julia> @btime all(x -> (0x30 <= x <= 0x39), codeunits(""))
  6.600 ns (0 allocations: 0 bytes)
true

```

[Next page](https://discourse.julialang.org/t/fast-check-if-a-character-is-an-integer/88750.md?page=2)
