Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write isNumeric function in Golang?

Tags:

go

I want to check if a string is numeric.

For example:

  • "abcd123" should return false.
  • "1.4" or "240" should return true.

I thought about using ParseInt and ParseFloat (from the strconv package), but am not sure if that is the right way.

like image 801
Crusaderpyro Avatar asked Nov 27 '22 07:11

Crusaderpyro


2 Answers

I was thinking of using strconv ParseInt and ParseFloat but not sure if that is the right way.

Well, it's certainly a right way.

You don't need to use ParseInt, though. ParseFloat will do the job.

func isNumeric(s string) bool {
    _, err := strconv.ParseFloat(s, 64)
    return err == nil
}

See an example here: https://play.golang.org/p/D53HRS-KIL

like image 84
WaltPurvis Avatar answered Feb 15 '23 12:02

WaltPurvis


If you need to convert the string to a floating-point number strconv.ParseFloat is the first choice.
Here you just need to know that there is only "0123456789" and maximum one '.' in your string, here for me isNumDot is 12x faster than isNumeric, see:
Consider this (1.7 seconds) - optimized for performance:

func isNumDot(s string) bool {
    dotFound := false
    for _, v := range s {
        if v == '.' {
            if dotFound {
                return false
            }
            dotFound = true
        } else if v < '0' || v > '9' {
            return false
        }
    }
    return true
}

and this (21.7 seconds - doing more extra works "converts the string to a floating-point number"):

func isNumeric(s string) bool {
    _, err := strconv.ParseFloat(s, 64)
    return err == nil
}

Try it:

package main

import (
    "fmt"
    "strconv"
    "time"
)

func isNumDot(s string) bool {
    dotFound := false
    for _, v := range s {
        if v == '.' {
            if dotFound {
                return false
            }
            dotFound = true
        } else if v < '0' || v > '9' {
            return false
        }
    }
    return true
}

func isNumeric(s string) bool {
    _, err := strconv.ParseFloat(s, 64)
    return err == nil
}

func main() {
    fmt.Println(isNumDot("240"))     //true
    fmt.Println(isNumDot("abcd123")) //false
    fmt.Println(isNumDot("0.4."))    //false
    fmt.Println(isNumDot("240 "))    //false
    benchmark(isNumDot)
    benchmark(isNumeric)
}

func benchmark(f func(string) bool) {
    var res bool
    t := time.Now()
    for i := 0; i < 100000000; i++ {
        res = f("a 240") || f("abcd123") || f("0.4.") || f("240 ")
    }
    fmt.Println(time.Since(t))
    fmt.Println(res)
}

output:

true
false
false
false
1.7822s
false
21.723s
false

Using the benchmark (isNumDot is faster than isNumeric):

BenchmarkIsNumDot-8     34117197            31.2 ns/op         0 B/op          0 allocs/op
BenchmarkIsNumeric-8     1931089           630 ns/op         192 B/op          4 allocs/op

// r = isNumDot("2.22")
BenchmarkIsNumDot-8     102849996           11.4 ns/op         0 B/op          0 allocs/op
BenchmarkIsNumeric-8    21994874            48.5 ns/op         0 B/op          0 allocs/op

// r = isNumDot("a 240")
BenchmarkIsNumDot-8     256610877            4.58 ns/op        0 B/op          0 allocs/op
BenchmarkIsNumeric-8     8962381           140 ns/op          48 B/op          1 allocs/op

The benchmark:

package main

import (
    "testing"
)

var r bool

func BenchmarkIsNumDot(b *testing.B) {
    for i := 0; i < b.N; i++ {
        r = isNumDot("a 240") || isNumDot("abcd123") || isNumDot("0.4.") || isNumDot("240 ")
    }
}

func BenchmarkIsNumeric(b *testing.B) {
    for i := 0; i < b.N; i++ {
        r = isNumeric("a 240") || isNumeric("abcd123") || isNumeric("0.4.") || isNumeric("240 ")
    }
}
like image 44
wasmup Avatar answered Feb 15 '23 10:02

wasmup