[go] internal/strconv: check for digits first in readFloat

2 views
Skip to first unread message

Gerrit Bot (Gerrit)

unread,
Aug 10, 2026, 9:10:58 AM (2 days ago) Aug 10
to goph...@pubsubhelper.golang.org, David Teather, golang-co...@googlegroups.com

Gerrit Bot has uploaded the change for review

Commit message

internal/strconv: check for digits first in readFloat

readFloat scans a float literal one byte at a time through a switch whose
arms were ordered rarest first. Every ordinary digit tested two rarer
cases and missed before matching. Reorder the arms so the two digit cases
come first. The case bodies are unchanged.

The package had no benchmark for hex or underscore input, so two are
added, and the existing benchmarks are converted to b.Loop.

goos: darwin
goarch: arm64
pkg: internal/strconv
cpu: Apple M2 Pro
│ old │ new │
│ sec/op │ sec/op vs base │
Atof64Decimal-10 18.38n ± 0% 15.12n ± 0% -17.74% (p=0.000 n=25)
Atof64Float-10 22.39n ± 1% 18.11n ± 0% -19.12% (p=0.000 n=25)
Atof64FloatExp-10 23.74n ± 0% 22.31n ± 0% -6.02% (p=0.000 n=25)
Atof64Big-10 58.92n ± 0% 42.75n ± 1% -27.44% (p=0.000 n=25)
Atof64RandomBits-10 63.19n ± 0% 53.62n ± 1% -15.14% (p=0.000 n=25)
Atof64RandomFloats-10 57.59n ± 0% 46.36n ± 0% -19.50% (p=0.000 n=25)
Atof64RandomLongFloats-10 72.98n ± 1% 63.56n ± 0% -12.91% (p=0.000 n=25)
Atof64Hex-10 38.01n ± 0% 35.51n ± 0% -6.58% (p=0.000 n=25)
Atof64Underscores-10 55.96n ± 0% 58.54n ± 0% +4.61% (p=0.000 n=25)
Atof32Decimal-10 17.81n ± 0% 14.53n ± 1% -18.42% (p=0.000 n=25)
Atof32Float-10 20.87n ± 0% 17.05n ± 0% -18.30% (p=0.000 n=25)
Atof32FloatExp-10 26.29n ± 0% 23.01n ± 0% -12.48% (p=0.000 n=25)
Atof32Random-10 37.93n ± 0% 34.25n ± 0% -9.70% (p=0.000 n=25)
Atof32RandomLong-10 64.19n ± 0% 55.40n ± 1% -13.69% (p=0.000 n=25)
geomean 36.60n 31.45n -14.06%

The linux/amd64 numbers below are from a shared 2-vCPU cloud instance and
are noisy, they should be treated as directional only. The smaller win is
expected because readFloat drops 25 instructions on arm64 against 10 on
amd64, since the reorder also removes a register shuffle the compiler
emits at the loop head.

goos: linux
goarch: amd64
pkg: internal/strconv
cpu: AMD EPYC 7763 64-Core Processor
│ old │ new │
│ sec/op │ sec/op vs base │
Atof64Decimal-2 32.41n ± 2% 28.59n ± 3% -11.79% (p=0.000 n=25)
Atof64Float-2 39.01n ± 3% 36.12n ± 3% -7.41% (p=0.000 n=25)
Atof64FloatExp-2 39.11n ± 4% 37.67n ± 3% -3.68% (p=0.003 n=25)
Atof64Big-2 99.93n ± 2% 79.83n ± 4% -20.11% (p=0.000 n=25)
Atof64RandomBits-2 100.70n ± 4% 89.12n ± 3% -11.50% (p=0.000 n=25)
Atof64RandomFloats-2 86.94n ± 5% 80.81n ± 4% -7.05% (p=0.000 n=25)
Atof64RandomLongFloats-2 121.8n ± 3% 117.3n ± 2% -3.69% (p=0.000 n=25)
Atof64Hex-2 67.69n ± 3% 64.36n ± 3% -4.92% (p=0.000 n=25)
Atof64Underscores-2 83.52n ± 3% 84.34n ± 4% ~ (p=0.200 n=25)
Atof32Decimal-2 29.56n ± 3% 27.17n ± 4% -8.09% (p=0.000 n=25)
Atof32Float-2 33.92n ± 3% 33.88n ± 4% ~ (p=0.229 n=25)
Atof32FloatExp-2 42.65n ± 3% 41.88n ± 4% -1.81% (p=0.038 n=25)
Atof32Random-2 61.15n ± 2% 58.62n ± 3% -4.14% (p=0.001 n=25)
Atof32RandomLong-2 107.70n ± 3% 99.94n ± 3% -7.21% (p=0.000 n=25)
geomean 60.29n 56.30n -6.62%

B/op and allocs/op are unchanged. Input that is mostly underscore
separators is about 5% slower, since that arm moved from first to last.

Updates #66327
Change-Id: Ifaaecc9891cbc3d7f107a2fff79a55884e8c8d84
GitHub-Last-Rev: 5f4ae844c7390f31d73ad30ad5cb8191e7588c33
GitHub-Pull-Request: golang/go#80813

Change diff

diff --git a/src/internal/strconv/atof.go b/src/internal/strconv/atof.go
index 4567af3..64acd46 100644
--- a/src/internal/strconv/atof.go
+++ b/src/internal/strconv/atof.go
@@ -213,18 +213,8 @@
loop:
for ; i < len(s); i++ {
switch c := s[i]; true {
- case c == '_':
- underscores = true
- continue
-
- case c == '.':
- if sawdot {
- break loop
- }
- sawdot = true
- dp = nd
- continue
-
+ // Digits are by far the most common character here,
+ // so check for them first.
case '0' <= c && c <= '9':
sawdigits = true
if c == '0' && nd == 0 { // ignore leading zeros
@@ -252,6 +242,18 @@
trunc = true
}
continue
+
+ case c == '.':
+ if sawdot {
+ break loop
+ }
+ sawdot = true
+ dp = nd
+ continue
+
+ case c == '_':
+ underscores = true
+ continue
}
break
}
diff --git a/src/internal/strconv/atof_test.go b/src/internal/strconv/atof_test.go
index 05ac9e6..3c5135a 100644
--- a/src/internal/strconv/atof_test.go
+++ b/src/internal/strconv/atof_test.go
@@ -646,25 +646,25 @@
}

func BenchmarkAtof64Decimal(b *testing.B) {
- for i := 0; i < b.N; i++ {
+ for b.Loop() {
ParseFloat("33909", 64)
}
}

func BenchmarkAtof64Float(b *testing.B) {
- for i := 0; i < b.N; i++ {
+ for b.Loop() {
ParseFloat("339.7784", 64)
}
}

func BenchmarkAtof64FloatExp(b *testing.B) {
- for i := 0; i < b.N; i++ {
+ for b.Loop() {
ParseFloat("-5.09e75", 64)
}
}

func BenchmarkAtof64Big(b *testing.B) {
- for i := 0; i < b.N; i++ {
+ for b.Loop() {
ParseFloat("123456789123456789123456789", 64)
}
}
@@ -672,16 +672,20 @@
func BenchmarkAtof64RandomBits(b *testing.B) {
initAtof()
b.ResetTimer()
- for i := 0; i < b.N; i++ {
+ i := 0
+ for b.Loop() {
ParseFloat(benchmarksRandomBits[i%1024], 64)
+ i++
}
}

func BenchmarkAtof64RandomFloats(b *testing.B) {
initAtof()
b.ResetTimer()
- for i := 0; i < b.N; i++ {
+ i := 0
+ for b.Loop() {
ParseFloat(benchmarksRandomNormal[i%1024], 64)
+ i++
}
}

@@ -693,7 +697,7 @@
}
b.ResetTimer()
idx := 0
- for i := 0; i < b.N; i++ {
+ for b.Loop() {
ParseFloat(samples[idx], 64)
idx++
if idx == len(samples) {
@@ -702,20 +706,32 @@
}
}

+func BenchmarkAtof64Hex(b *testing.B) {
+ for b.Loop() {
+ ParseFloat("0x1.fp-2", 64)
+ }
+}
+
+func BenchmarkAtof64Underscores(b *testing.B) {
+ for b.Loop() {
+ ParseFloat("1_2_3_4_5_6_7_8.9_1", 64)
+ }
+}
+
func BenchmarkAtof32Decimal(b *testing.B) {
- for i := 0; i < b.N; i++ {
+ for b.Loop() {
ParseFloat("33909", 32)
}
}

func BenchmarkAtof32Float(b *testing.B) {
- for i := 0; i < b.N; i++ {
+ for b.Loop() {
ParseFloat("339.778", 32)
}
}

func BenchmarkAtof32FloatExp(b *testing.B) {
- for i := 0; i < b.N; i++ {
+ for b.Loop() {
ParseFloat("12.3456e32", 32)
}
}
@@ -728,8 +744,10 @@
float32strings[i] = FormatFloat(float64(math.Float32frombits(n)), 'g', -1, 32)
}
b.ResetTimer()
- for i := 0; i < b.N; i++ {
+ i := 0
+ for b.Loop() {
ParseFloat(float32strings[i%4096], 32)
+ i++
}
}

@@ -741,7 +759,9 @@
float32strings[i] = FormatFloat(float64(math.Float32frombits(n)), 'g', 20, 32)
}
b.ResetTimer()
- for i := 0; i < b.N; i++ {
+ i := 0
+ for b.Loop() {
ParseFloat(float32strings[i%4096], 32)
+ i++
}
}

Change information

Files:
  • M src/internal/strconv/atof.go
  • M src/internal/strconv/atof_test.go
Change size: M
Delta: 2 files changed, 46 insertions(+), 24 deletions(-)
Open in Gerrit

Related details

Attention set is empty
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: newchange
Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: Ifaaecc9891cbc3d7f107a2fff79a55884e8c8d84
Gerrit-Change-Number: 812660
Gerrit-PatchSet: 1
Gerrit-Owner: Gerrit Bot <letsus...@gmail.com>
Gerrit-CC: David Teather <contact.da...@gmail.com>
unsatisfied_requirement
satisfied_requirement
open
diffy

David Teather (Gerrit)

unread,
Aug 10, 2026, 9:18:26 AM (2 days ago) Aug 10
to Gerrit Bot, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

David Teather added 1 comment

Patchset-level comments
File-level comment, Patchset 1 (Latest):
David Teather . unresolved

I want to double check that my benchmarks are after the b.Loop() modernization changes when I get home today. I think at least the Mac comparison was but need to verify the Linux one.

Open in Gerrit

Related details

Attention set is empty
Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement is not satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-Change-Id: Ifaaecc9891cbc3d7f107a2fff79a55884e8c8d84
    Gerrit-Change-Number: 812660
    Gerrit-PatchSet: 1
    Gerrit-Owner: Gerrit Bot <letsus...@gmail.com>
    Gerrit-CC: David Teather <contact.da...@gmail.com>
    Gerrit-Comment-Date: Mon, 10 Aug 2026 13:18:19 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    unsatisfied_requirement
    open
    diffy

    David Teather (Gerrit)

    unread,
    Aug 10, 2026, 9:51:21 PM (2 days ago) Aug 10
    to Gerrit Bot, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

    David Teather added 1 comment

    Patchset-level comments
    David Teather . resolved

    I want to double check that my benchmarks are after the b.Loop() modernization changes when I get home today. I think at least the Mac comparison was but need to verify the Linux one.

    David Teather

    Verified that they were both taken post-b.Loop() modernization.

    Open in Gerrit

    Related details

    Attention set is empty
    Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: Ifaaecc9891cbc3d7f107a2fff79a55884e8c8d84
      Gerrit-Change-Number: 812660
      Gerrit-PatchSet: 1
      Gerrit-Owner: Gerrit Bot <letsus...@gmail.com>
      Gerrit-CC: David Teather <contact.da...@gmail.com>
      Gerrit-Comment-Date: Tue, 11 Aug 2026 01:51:15 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: No
      Comment-In-Reply-To: David Teather <contact.da...@gmail.com>
      unsatisfied_requirement
      satisfied_requirement
      open
      diffy

      Robert Griesemer (Gerrit)

      unread,
      Aug 11, 2026, 12:57:20 PM (15 hours ago) Aug 11
      to David Teather, Gerrit Bot, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

      Robert Griesemer voted Commit-Queue+1

      Commit-Queue+1
      Open in Gerrit

      Related details

      Attention set is empty
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: Ifaaecc9891cbc3d7f107a2fff79a55884e8c8d84
      Gerrit-Change-Number: 812660
      Gerrit-PatchSet: 1
      Gerrit-Owner: Gerrit Bot <letsus...@gmail.com>
      Gerrit-Reviewer: Robert Griesemer <g...@google.com>
      Gerrit-CC: David Teather <contact.da...@gmail.com>
      Gerrit-Comment-Date: Tue, 11 Aug 2026 16:57:15 +0000
      Gerrit-HasComments: No
      Gerrit-Has-Labels: Yes
      unsatisfied_requirement
      satisfied_requirement
      open
      diffy

      Robert Griesemer (Gerrit)

      unread,
      Aug 11, 2026, 1:26:51 PM (14 hours ago) Aug 11
      to David Teather, Gerrit Bot, goph...@pubsubhelper.golang.org, golang...@luci-project-accounts.iam.gserviceaccount.com, golang-co...@googlegroups.com

      Robert Griesemer voted and added 2 comments

      Votes added by Robert Griesemer

      Auto-Submit+1
      Code-Review+2

      2 comments

      Patchset-level comments
      Robert Griesemer . resolved

      Nice, thanks!
      LGTM but please address the minor grammar mistake.

      File src/internal/strconv/atof.go
      Line 216, Patchset 1 (Latest): // Digits are by far the most common character here,
      Robert Griesemer . unresolved

      s/character/characters/

      also, feel free to make this just one line (no need for line break)

      Open in Gerrit

      Related details

      Attention set is empty
      Submit Requirements:
      • requirement satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: Ifaaecc9891cbc3d7f107a2fff79a55884e8c8d84
      Gerrit-Change-Number: 812660
      Gerrit-PatchSet: 1
      Gerrit-Owner: Gerrit Bot <letsus...@gmail.com>
      Gerrit-Reviewer: Robert Griesemer <g...@google.com>
      Gerrit-CC: David Teather <contact.da...@gmail.com>
      Gerrit-Comment-Date: Tue, 11 Aug 2026 17:26:46 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: Yes
      satisfied_requirement
      unsatisfied_requirement
      open
      diffy

      Gerrit Bot (Gerrit)

      unread,
      Aug 11, 2026, 8:28:37 PM (7 hours ago) Aug 11
      to David Teather, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
      Attention needed from Robert Griesemer

      Gerrit Bot uploaded new patchset

      Gerrit Bot uploaded patch set #2 to this change.
      Following approvals got outdated and were removed:
      Open in Gerrit

      Related details

      Attention is currently required from:
      • Robert Griesemer
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: newpatchset
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: Ifaaecc9891cbc3d7f107a2fff79a55884e8c8d84
      Gerrit-Change-Number: 812660
      Gerrit-PatchSet: 2
      Gerrit-Owner: Gerrit Bot <letsus...@gmail.com>
      Gerrit-Reviewer: Robert Griesemer <g...@google.com>
      Gerrit-CC: David Teather <contact.da...@gmail.com>
      Gerrit-Attention: Robert Griesemer <g...@google.com>
      unsatisfied_requirement
      open
      diffy

      David Teather (Gerrit)

      unread,
      Aug 11, 2026, 8:29:39 PM (7 hours ago) Aug 11
      to Gerrit Bot, goph...@pubsubhelper.golang.org, Robert Griesemer, golang...@luci-project-accounts.iam.gserviceaccount.com, golang-co...@googlegroups.com
      Attention needed from Robert Griesemer

      David Teather added 2 comments

      Patchset-level comments
      Robert Griesemer . resolved

      Nice, thanks!
      LGTM but please address the minor grammar mistake.

      David Teather

      Updated, thanks for the review!

      File src/internal/strconv/atof.go
      Line 216, Patchset 1: // Digits are by far the most common character here,
      Robert Griesemer . resolved

      s/character/characters/

      also, feel free to make this just one line (no need for line break)

      David Teather

      Updated to this and made it one line. thanks!
      > // Digits are by far the most common characters here, so check for them first.

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Robert Griesemer
      Submit Requirements:
        • requirement is not satisfiedCode-Review
        • requirement satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        • requirement is not satisfiedTryBots-Pass
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: go
        Gerrit-Branch: master
        Gerrit-Change-Id: Ifaaecc9891cbc3d7f107a2fff79a55884e8c8d84
        Gerrit-Change-Number: 812660
        Gerrit-PatchSet: 2
        Gerrit-Owner: Gerrit Bot <letsus...@gmail.com>
        Gerrit-Reviewer: Robert Griesemer <g...@google.com>
        Gerrit-CC: David Teather <contact.da...@gmail.com>
        Gerrit-Attention: Robert Griesemer <g...@google.com>
        Gerrit-Comment-Date: Wed, 12 Aug 2026 00:29:35 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Robert Griesemer <g...@google.com>
        unsatisfied_requirement
        satisfied_requirement
        open
        diffy
        Reply all
        Reply to author
        Forward
        0 new messages