title: Most efficient way to check if a byte slice is a number
body: <p>I'm looking for the most efficient way to tell whether a byte slice is a float. </p>
  
  <p>This is to be done on huge datasets, so performance is key.</p>
  
  <p>Tried approaches:</p>
  
  <ul>
  <li><code>strconv.ParseFloat</code></li>
  <li><code>regexp.Match</code></li>
  <li><p><code>CheckNumber</code> - home rolled function using <code>IsNumber</code> + looking at whether the byte slice contains a <code>.</code>.</p>
  
  <pre><code>func CheckNumber(p []byte) bool {
      r := string(p)
      sep := 0
      for _, b := range r {
          if unicode.IsNumber(b) {
              continue
          }
          if b == rune('.') {
              if sep &gt; 0 {
                  return false
              }
              sep++
              continue
          }
          return false
      }
      return true
  }
  </code></pre></li>
  </ul>
  
  <p>The benchmark code:</p>
  
  <pre><code>func BenchmarkFloatStrconv(b *testing.B) {
      p := []byte("15.34234234234")
  
      for i := 0; i &lt; 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 &lt; 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 &lt; b.N; i++ {
          ok := CheckNumber(p)
          if !ok {
              log.Fatalf("NaN")
          }
      }
  }
  </code></pre>
  
  <p>Benchmark results:</p>
  
  <pre><code>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
  </code></pre>
  
  <ul>
  <li>Am I doing the different solutions fairness?</li>
  <li>Are there better solutions?</li>
  </ul>
  
  <p><strong>Edit:</strong> thanks to pointers from Adrian and icza, this avoids converting to <code>strings</code>/<code>rune</code>s</p>
  
  <pre><code>func CheckNumberNoStringConvert(r []byte) bool {
      sep := 0
  
      for i := range r {
          if r[i] &gt;= 48 &amp;&amp; r[i] &lt;= 57 {
              continue
          }
          if r[i] == 46 {
              if sep &gt; 0 {
                  return false
              }
              sep++
              continue
          }
          return false
      }
  
      return true
  }
  </code></pre>
  
  <p>and performs quite well ;-)</p>
  
  <pre><code>BenchmarkCheckNumberNoStringConvert-8       200000000            8.55 ns/op        0 B/op          0 allocs/op
  </code></pre>