检查字节切片是否为数字的最有效方法

sal*_*ent 5 go

我正在寻找一种最有效的方法来判断字节切片是否为浮点数.

这是在大型数据集上完成的,因此性能至关重要.

尝试的方法:

  • strconv.ParseFloat
  • regexp.Match
  • CheckNumber- home roll函数使用IsNumber+查看字节切片是否包含a ..

    func CheckNumber(p []byte) bool {
        r := string(p)
        sep := 0
        for _, b := range r {
            if unicode.IsNumber(b) {
                continue
            }
            if b == rune('.') {
                if sep > 0 {
                    return false
                }
                sep++
                continue
            }
            return false
        }
        return true
    }
    
    Run Code Online (Sandbox Code Playgroud)

基准代码:

func BenchmarkFloatStrconv(b *testing.B) {
    p := []byte("15.34234234234")

    for i := 0; i < b.N; i++ {
        _, err := strconv.ParseFloat(string(p), 64)
        if err != nil {
            log.Fatalf("NaN")
        }
    }
}

func BenchmarkFloatRegex(b *testing.B) {
    p := []byte("15.34234234234")
    r := `[-+]?[0-9]*\.?[0-9]`
    c, _ := regexp.Compile(r)

    for i := 0; i < b.N; i++ {
        ok := c.Match(p)
        if !ok {
            log.Fatalf("NaN")
        }
    }
}

func BenchmarkCheckNumber(b *testing.B) {
    p := []byte("15.34234234234")

    for i := 0; i < b.N; i++ {
        ok := CheckNumber(p)
        if !ok {
            log.Fatalf("NaN")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

基准测试结果:

BenchmarkFloatStrconv-8     20000000            85.8 ns/op        16 B/op          1 allocs/op
BenchmarkFloatRegex-8        5000000           252 ns/op           0 B/op          0 allocs/op
BenchmarkCheckNumber-8      20000000            64.3 ns/op         0 B/op          0 allocs/op
Run Code Online (Sandbox Code Playgroud)
  • 我在做不同的解决方案公平吗?
  • 有更好的解决方案吗?

编辑:感谢Adrian和icza的指针,这可以避免转换为strings/ runes

func CheckNumberNoStringConvert(r []byte) bool {
    sep := 0

    for i := range r {
        if r[i] >= 48 && r[i] <= 57 {
            continue
        }
        if r[i] == 46 {
            if sep > 0 {
                return false
            }
            sep++
            continue
        }
        return false
    }

    return true
}
Run Code Online (Sandbox Code Playgroud)

并表现得相当好;-)

BenchmarkCheckNumberNoStringConvert-8       200000000            8.55 ns/op        0 B/op          0 allocs/op
Run Code Online (Sandbox Code Playgroud)

pet*_*rSO 3

对于简单的实数(浮点)数(没有科学或工程浮点格式,没有组分隔符),

func IsReal(n []byte) bool {
    if len(n) > 0 && n[0] == '-' {
        n = n[1:]
    }
    if len(n) == 0 {
        return false
    }
    var point bool
    for _, c := range n {
        if '0' <= c && c <= '9' {
            continue
        }
        if c == '.' && len(n) > 1 && !point {
            point = true
            continue
        }
        return false
    }
    return true
}
Run Code Online (Sandbox Code Playgroud)

基准:

$ go test -run=! -bench=. -benchmem -cpu=1 real_test.go
goos: linux
goarch: amd64
BenchmarkIsReal         100000000       20.8 ns/op         0 B/op          0 allocs/op
BenchmarkFloatStrconv   20000000       101 ns/op          16 B/op          1 allocs/op
BenchmarkFloatRegex      5000000       284 ns/op           0 B/op          0 allocs/op
BenchmarkCheckNumber    20000000        73.0 ns/op         0 B/op          0 allocs/op
PASS
ok      command-line-arguments  7.380s
Run Code Online (Sandbox Code Playgroud)

real_test.go

package main

import (
    "log"
    "regexp"
    "strconv"
    "testing"
    "unicode"
)

func IsReal(n []byte) bool {
    if len(n) > 0 && n[0] == '-' {
        n = n[1:]
    }
    if len(n) == 0 {
        return false
    }
    var point bool
    for _, c := range n {
        if '0' <= c && c <= '9' {
            continue
        }
        if c == '.' && len(n) > 1 && !point {
            point = true
            continue
        }
        return false
    }
    return true
}

func BenchmarkIsReal(b *testing.B) {
    p := []byte("15.34234234234")
    for i := 0; i < b.N; i++ {
        ok := IsReal(p)
        if !ok {
            log.Fatalf("NaN")
        }
    }
}

func CheckNumber(p []byte) bool {
    r := string(p)

    sep := 0

    for _, b := range r {
        if unicode.IsNumber(b) {
            continue
        }
        if b == rune('.') {
            if sep > 0 {
                return false
            }
            sep++
            continue
        }
        return false
    }

    return true

}

func BenchmarkFloatStrconv(b *testing.B) {
    p := []byte("15.34234234234")

    for i := 0; i < b.N; i++ {
        _, err := strconv.ParseFloat(string(p), 64)
        if err != nil {
            log.Fatalf("NaN")
        }
    }
}

func BenchmarkFloatRegex(b *testing.B) {
    p := []byte("15.34234234234")
    r := `[-+]?[0-9]*\.?[0-9]`
    c, _ := regexp.Compile(r)

    for i := 0; i < b.N; i++ {
        ok := c.Match(p)
        if !ok {
            log.Fatalf("NaN")
        }
    }
}

func BenchmarkCheckNumber(b *testing.B) {
    p := []byte("15.34234234234")

    for i := 0; i < b.N; i++ {
        ok := CheckNumber(p)
        if !ok {
            log.Fatalf("NaN")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)