Skip to content

Commit dd839f1

Browse files
committed
internal/strconv: handle %f with fixedFtoa when possible
Everyone writes papers about fast shortest-output formatting. Eventually we also sped up fixed-length formatting %e and %g. But we've neglected %f, which falls back to the slow general code even for relatively trivial things like %.2f on 1.23. This CL uses the fast path fixedFtoa for %f when possible by estimating the number of digits needed. benchmark \ host linux-arm64 local linux-amd64 s7 linux-386 s7:GOARCH=386 vs base vs base vs base vs base vs base vs base AppendFloat/Decimal ~ ~ ~ +0.30% ~ ~ AppendFloat/Float -0.45% ~ -2.20% ~ -2.19% ~ AppendFloat/Exp +0.12% ~ +4.11% ~ ~ ~ AppendFloat/NegExp +0.53% ~ ~ ~ ~ ~ AppendFloat/LongExp +0.41% -1.42% +4.50% ~ ~ ~ AppendFloat/Big ~ -1.25% +3.69% ~ ~ ~ AppendFloat/BinaryExp +0.38% +1.68% ~ ~ +2.65% +0.97% AppendFloat/32Integer ~ ~ ~ ~ ~ ~ AppendFloat/32ExactFraction ~ ~ -2.61% ~ ~ ~ AppendFloat/32Point -0.41% ~ -2.65% ~ ~ ~ AppendFloat/32Exp ~ ~ +5.35% ~ +1.44% +0.39% AppendFloat/32NegExp +0.30% ~ +2.31% ~ ~ +0.82% AppendFloat/32Shortest +0.28% -0.85% ~ ~ -3.20% ~ AppendFloat/32Fixed8Hard -0.29% ~ ~ -1.75% ~ +4.30% AppendFloat/32Fixed9Hard ~ ~ ~ ~ ~ +1.52% AppendFloat/64Fixed1 +0.61% -2.03% ~ ~ ~ +4.36% AppendFloat/64Fixed2 ~ -3.43% ~ ~ ~ +1.03% AppendFloat/64Fixed2.5 +0.57% -2.23% ~ ~ ~ +2.66% AppendFloat/64Fixed3 ~ -1.64% ~ +0.31% +2.32% +2.10% AppendFloat/64Fixed4 +0.15% -2.11% ~ ~ +1.48% +1.58% AppendFloat/64Fixed5Hard +0.45% ~ +1.58% ~ ~ +1.73% AppendFloat/64Fixed12 -0.16% ~ +1.63% -1.23% +3.93% +2.42% AppendFloat/64Fixed16 -0.33% -0.49% ~ ~ +3.67% +2.33% AppendFloat/64Fixed12Hard -0.58% ~ ~ ~ +4.98% +0.62% AppendFloat/64Fixed17Hard +0.27% -0.94% ~ ~ +2.07% +1.79% AppendFloat/64Fixed18Hard ~ ~ ~ ~ ~ ~ AppendFloat/64FixedF1 -69.59% -76.08% -70.94% -68.26% -75.27% -69.88% AppendFloat/64FixedF2 -76.28% -81.82% -76.95% -77.34% -83.53% -80.04% AppendFloat/64FixedF3 -77.30% -84.51% -77.82% -77.81% -78.77% -73.69% AppendFloat/Slowpath64 ~ -1.30% +1.64% ~ -2.66% -0.44% AppendFloat/SlowpathDenormal64 +0.11% -1.69% ~ ~ -2.90% ~ host: linux-arm64 goos: linux goarch: arm64 pkg: internal/strconv cpu: unknown │ 1cc918cc725 │ b66c604f523 │ │ sec/op │ sec/op vs base │ AppendFloat/Decimal-8 60.22n ± 0% 60.21n ± 0% ~ (p=0.416 n=20) AppendFloat/Float-8 88.93n ± 0% 88.53n ± 0% -0.45% (p=0.000 n=20) AppendFloat/Exp-8 93.09n ± 0% 93.20n ± 0% +0.12% (p=0.000 n=20) AppendFloat/NegExp-8 93.06n ± 0% 93.56n ± 0% +0.53% (p=0.000 n=20) AppendFloat/LongExp-8 99.79n ± 0% 100.20n ± 0% +0.41% (p=0.000 n=20) AppendFloat/Big-8 103.9n ± 0% 104.0n ± 0% ~ (p=0.004 n=20) AppendFloat/BinaryExp-8 47.34n ± 0% 47.52n ± 0% +0.38% (p=0.000 n=20) AppendFloat/32Integer-8 60.43n ± 0% 60.40n ± 0% ~ (p=0.006 n=20) AppendFloat/32ExactFraction-8 86.21n ± 0% 86.24n ± 0% ~ (p=0.634 n=20) AppendFloat/32Point-8 83.20n ± 0% 82.87n ± 0% -0.41% (p=0.000 n=20) AppendFloat/32Exp-8 89.43n ± 0% 89.45n ± 0% ~ (p=0.193 n=20) AppendFloat/32NegExp-8 87.31n ± 0% 87.58n ± 0% +0.30% (p=0.000 n=20) AppendFloat/32Shortest-8 76.28n ± 0% 76.49n ± 0% +0.28% (p=0.000 n=20) AppendFloat/32Fixed8Hard-8 52.44n ± 0% 52.29n ± 0% -0.29% (p=0.000 n=20) AppendFloat/32Fixed9Hard-8 60.57n ± 0% 60.54n ± 0% ~ (p=0.285 n=20) AppendFloat/64Fixed1-8 46.27n ± 0% 46.55n ± 0% +0.61% (p=0.000 n=20) AppendFloat/64Fixed2-8 46.77n ± 0% 46.80n ± 0% ~ (p=0.060 n=20) AppendFloat/64Fixed2.5-8 43.70n ± 0% 43.95n ± 0% +0.57% (p=0.000 n=20) AppendFloat/64Fixed3-8 47.22n ± 0% 47.19n ± 0% ~ (p=0.008 n=20) AppendFloat/64Fixed4-8 44.07n ± 0% 44.13n ± 0% +0.15% (p=0.000 n=20) AppendFloat/64Fixed5Hard-8 51.81n ± 0% 52.04n ± 0% +0.45% (p=0.000 n=20) AppendFloat/64Fixed12-8 78.41n ± 0% 78.29n ± 0% -0.16% (p=0.000 n=20) AppendFloat/64Fixed16-8 65.14n ± 0% 64.93n ± 0% -0.33% (p=0.000 n=20) AppendFloat/64Fixed12Hard-8 62.12n ± 0% 61.76n ± 0% -0.58% (p=0.000 n=20) AppendFloat/64Fixed17Hard-8 73.93n ± 0% 74.13n ± 0% +0.27% (p=0.000 n=20) AppendFloat/64Fixed18Hard-8 4.285µ ± 0% 4.283µ ± 0% ~ (p=0.039 n=20) AppendFloat/64FixedF1-8 216.10n ± 0% 65.71n ± 0% -69.59% (p=0.000 n=20) AppendFloat/64FixedF2-8 227.70n ± 0% 54.02n ± 0% -76.28% (p=0.000 n=20) AppendFloat/64FixedF3-8 208.20n ± 1% 47.25n ± 0% -77.30% (p=0.000 n=20) AppendFloat/Slowpath64-8 97.40n ± 0% 97.45n ± 0% ~ (p=0.018 n=20) AppendFloat/SlowpathDenormal64-8 94.75n ± 0% 94.86n ± 0% +0.11% (p=0.000 n=20) geomean 87.86n 76.99n -12.37% host: local goos: darwin cpu: Apple M3 Pro │ 1cc918cc725 │ b66c604f523 │ │ sec/op │ sec/op vs base │ AppendFloat/Decimal-12 21.05n ± 1% 20.91n ± 1% ~ (p=0.051 n=20) AppendFloat/Float-12 32.13n ± 0% 32.04n ± 1% ~ (p=0.457 n=20) AppendFloat/Exp-12 31.84n ± 0% 31.72n ± 0% ~ (p=0.151 n=20) AppendFloat/NegExp-12 31.78n ± 1% 31.79n ± 1% ~ (p=0.867 n=20) AppendFloat/LongExp-12 33.70n ± 0% 33.22n ± 1% -1.42% (p=0.000 n=20) AppendFloat/Big-12 35.52n ± 1% 35.07n ± 1% -1.25% (p=0.000 n=20) AppendFloat/BinaryExp-12 19.32n ± 1% 19.64n ± 0% +1.68% (p=0.000 n=20) AppendFloat/32Integer-12 21.32n ± 0% 21.18n ± 1% ~ (p=0.025 n=20) AppendFloat/32ExactFraction-12 30.88n ± 0% 31.07n ± 0% ~ (p=0.087 n=20) AppendFloat/32Point-12 30.88n ± 0% 30.95n ± 1% ~ (p=0.250 n=20) AppendFloat/32Exp-12 31.57n ± 0% 31.67n ± 2% ~ (p=0.126 n=20) AppendFloat/32NegExp-12 30.50n ± 1% 30.76n ± 1% ~ (p=0.087 n=20) AppendFloat/32Shortest-12 27.14n ± 0% 26.91n ± 1% -0.85% (p=0.001 n=20) AppendFloat/32Fixed8Hard-12 17.11n ± 0% 17.08n ± 0% ~ (p=0.027 n=20) AppendFloat/32Fixed9Hard-12 19.16n ± 1% 19.31n ± 1% ~ (p=0.062 n=20) AppendFloat/64Fixed1-12 15.50n ± 0% 15.18n ± 1% -2.03% (p=0.000 n=20) AppendFloat/64Fixed2-12 15.46n ± 0% 14.93n ± 0% -3.43% (p=0.000 n=20) AppendFloat/64Fixed2.5-12 15.28n ± 0% 14.94n ± 1% -2.23% (p=0.000 n=20) AppendFloat/64Fixed3-12 15.58n ± 0% 15.32n ± 1% -1.64% (p=0.000 n=20) AppendFloat/64Fixed4-12 15.39n ± 0% 15.06n ± 1% -2.11% (p=0.000 n=20) AppendFloat/64Fixed5Hard-12 18.00n ± 0% 18.07n ± 1% ~ (p=0.011 n=20) AppendFloat/64Fixed12-12 27.97n ± 8% 29.05n ± 3% ~ (p=0.107 n=20) AppendFloat/64Fixed16-12 21.48n ± 0% 21.38n ± 0% -0.49% (p=0.000 n=20) AppendFloat/64Fixed12Hard-12 20.79n ± 1% 21.05n ± 2% ~ (p=0.784 n=20) AppendFloat/64Fixed17Hard-12 27.21n ± 1% 26.95n ± 1% -0.94% (p=0.000 n=20) AppendFloat/64Fixed18Hard-12 2.166µ ± 1% 2.182µ ± 1% ~ (p=0.031 n=20) AppendFloat/64FixedF1-12 103.35n ± 0% 24.72n ± 0% -76.08% (p=0.000 n=20) AppendFloat/64FixedF2-12 114.30n ± 1% 20.78n ± 0% -81.82% (p=0.000 n=20) AppendFloat/64FixedF3-12 107.10n ± 0% 16.58n ± 0% -84.51% (p=0.000 n=20) AppendFloat/Slowpath64-12 32.01n ± 0% 31.59n ± 0% -1.30% (p=0.000 n=20) AppendFloat/SlowpathDenormal64-12 30.21n ± 0% 29.70n ± 0% -1.69% (p=0.000 n=20) geomean 31.84n 27.00n -15.20% host: linux-amd64 goos: linux goarch: amd64 cpu: Intel(R) Xeon(R) CPU @ 2.30GHz │ 1cc918cc725 │ b66c604f523 │ │ sec/op │ sec/op vs base │ AppendFloat/Decimal-16 63.62n ± 1% 64.05n ± 1% ~ (p=0.753 n=20) AppendFloat/Float-16 97.12n ± 1% 94.98n ± 1% -2.20% (p=0.000 n=20) AppendFloat/Exp-16 98.12n ± 1% 102.15n ± 1% +4.11% (p=0.000 n=20) AppendFloat/NegExp-16 101.1n ± 1% 101.5n ± 1% ~ (p=0.089 n=20) AppendFloat/LongExp-16 104.5n ± 1% 109.2n ± 1% +4.50% (p=0.000 n=20) AppendFloat/Big-16 108.5n ± 0% 112.5n ± 1% +3.69% (p=0.000 n=20) AppendFloat/BinaryExp-16 47.68n ± 1% 47.44n ± 1% ~ (p=0.143 n=20) AppendFloat/32Integer-16 63.77n ± 2% 63.45n ± 1% ~ (p=0.015 n=20) AppendFloat/32ExactFraction-16 97.69n ± 1% 95.14n ± 1% -2.61% (p=0.000 n=20) AppendFloat/32Point-16 92.17n ± 1% 89.72n ± 1% -2.65% (p=0.000 n=20) AppendFloat/32Exp-16 95.63n ± 1% 100.75n ± 1% +5.35% (p=0.000 n=20) AppendFloat/32NegExp-16 94.53n ± 1% 96.72n ± 0% +2.31% (p=0.000 n=20) AppendFloat/32Shortest-16 86.43n ± 0% 86.95n ± 0% ~ (p=0.010 n=20) AppendFloat/32Fixed8Hard-16 57.75n ± 1% 57.95n ± 1% ~ (p=0.098 n=20) AppendFloat/32Fixed9Hard-16 66.56n ± 2% 66.97n ± 1% ~ (p=0.380 n=20) AppendFloat/64Fixed1-16 51.02n ± 1% 50.99n ± 1% ~ (p=0.473 n=20) AppendFloat/64Fixed2-16 50.94n ± 1% 51.01n ± 1% ~ (p=0.136 n=20) AppendFloat/64Fixed2.5-16 49.27n ± 1% 49.37n ± 1% ~ (p=0.218 n=20) AppendFloat/64Fixed3-16 51.85n ± 1% 52.55n ± 1% ~ (p=0.045 n=20) AppendFloat/64Fixed4-16 50.30n ± 1% 50.43n ± 1% ~ (p=0.794 n=20) AppendFloat/64Fixed5Hard-16 57.57n ± 1% 58.48n ± 1% +1.58% (p=0.000 n=20) AppendFloat/64Fixed12-16 82.67n ± 1% 84.02n ± 1% +1.63% (p=0.000 n=20) AppendFloat/64Fixed16-16 71.10n ± 1% 70.94n ± 1% ~ (p=0.569 n=20) AppendFloat/64Fixed12Hard-16 68.36n ± 1% 68.64n ± 1% ~ (p=0.155 n=20) AppendFloat/64Fixed17Hard-16 80.16n ± 1% 80.10n ± 1% ~ (p=0.836 n=20) AppendFloat/64Fixed18Hard-16 4.916µ ± 1% 4.919µ ± 1% ~ (p=0.507 n=20) AppendFloat/64FixedF1-16 239.75n ± 1% 69.67n ± 1% -70.94% (p=0.000 n=20) AppendFloat/64FixedF2-16 252.50n ± 1% 58.20n ± 1% -76.95% (p=0.000 n=20) AppendFloat/64FixedF3-16 238.00n ± 1% 52.79n ± 1% -77.82% (p=0.000 n=20) AppendFloat/Slowpath64-16 100.4n ± 1% 102.0n ± 1% +1.64% (p=0.000 n=20) AppendFloat/SlowpathDenormal64-16 97.92n ± 1% 98.01n ± 1% ~ (p=0.304 n=20) geomean 95.58n 84.00n -12.12% host: s7 cpu: AMD Ryzen 9 7950X 16-Core Processor │ 1cc918cc725 │ b66c604f523 │ │ sec/op │ sec/op vs base │ AppendFloat/Decimal-32 22.00n ± 0% 22.06n ± 0% +0.30% (p=0.001 n=20) AppendFloat/Float-32 34.83n ± 0% 34.76n ± 0% ~ (p=0.159 n=20) AppendFloat/Exp-32 34.91n ± 0% 34.89n ± 0% ~ (p=0.188 n=20) AppendFloat/NegExp-32 35.24n ± 0% 35.32n ± 0% ~ (p=0.026 n=20) AppendFloat/LongExp-32 37.02n ± 0% 37.02n ± 0% ~ (p=0.317 n=20) AppendFloat/Big-32 38.51n ± 0% 38.43n ± 0% ~ (p=0.060 n=20) AppendFloat/BinaryExp-32 17.57n ± 0% 17.59n ± 0% ~ (p=0.278 n=20) AppendFloat/32Integer-32 22.06n ± 0% 22.09n ± 0% ~ (p=0.762 n=20) AppendFloat/32ExactFraction-32 32.91n ± 0% 33.00n ± 0% ~ (p=0.055 n=20) AppendFloat/32Point-32 33.24n ± 0% 33.18n ± 0% ~ (p=0.068 n=20) AppendFloat/32Exp-32 34.50n ± 0% 34.55n ± 0% ~ (p=0.030 n=20) AppendFloat/32NegExp-32 33.53n ± 0% 33.61n ± 0% ~ (p=0.045 n=20) AppendFloat/32Shortest-32 30.10n ± 0% 30.10n ± 0% ~ (p=0.931 n=20) AppendFloat/32Fixed8Hard-32 22.89n ± 0% 22.49n ± 0% -1.75% (p=0.000 n=20) AppendFloat/32Fixed9Hard-32 25.82n ± 0% 25.75n ± 1% ~ (p=0.143 n=20) AppendFloat/64Fixed1-32 18.80n ± 0% 18.70n ± 0% ~ (p=0.004 n=20) AppendFloat/64Fixed2-32 18.64n ± 1% 18.54n ± 0% ~ (p=0.001 n=20) AppendFloat/64Fixed2.5-32 17.89n ± 0% 17.81n ± 0% ~ (p=0.001 n=20) AppendFloat/64Fixed3-32 19.62n ± 0% 19.68n ± 0% +0.31% (p=0.000 n=20) AppendFloat/64Fixed4-32 18.64n ± 0% 18.82n ± 0% ~ (p=0.010 n=20) AppendFloat/64Fixed5Hard-32 21.62n ± 0% 21.57n ± 0% ~ (p=0.058 n=20) AppendFloat/64Fixed12-32 30.98n ± 1% 30.61n ± 1% -1.23% (p=0.000 n=20) AppendFloat/64Fixed16-32 26.89n ± 0% 27.08n ± 1% ~ (p=0.003 n=20) AppendFloat/64Fixed12Hard-32 26.03n ± 0% 26.20n ± 1% ~ (p=0.344 n=20) AppendFloat/64Fixed17Hard-32 30.03n ± 1% 29.72n ± 1% ~ (p=0.001 n=20) AppendFloat/64Fixed18Hard-32 1.824µ ± 0% 1.825µ ± 1% ~ (p=0.567 n=20) AppendFloat/64FixedF1-32 83.58n ± 1% 26.52n ± 0% -68.26% (p=0.000 n=20) AppendFloat/64FixedF2-32 89.68n ± 1% 20.32n ± 1% -77.34% (p=0.000 n=20) AppendFloat/64FixedF3-32 84.84n ± 0% 18.82n ± 0% -77.81% (p=0.000 n=20) AppendFloat/Slowpath64-32 35.55n ± 0% 35.61n ± 0% ~ (p=0.394 n=20) AppendFloat/SlowpathDenormal64-32 35.03n ± 0% 35.02n ± 0% ~ (p=0.733 n=20) geomean 34.67n 30.31n -12.56% host: linux-386 goarch: 386 cpu: Intel(R) Xeon(R) CPU @ 2.30GHz │ 1cc918cc725 │ b66c604f523 │ │ sec/op │ sec/op vs base │ AppendFloat/Decimal-16 133.6n ± 1% 130.5n ± 1% ~ (p=0.002 n=20) AppendFloat/Float-16 242.3n ± 1% 237.0n ± 1% -2.19% (p=0.000 n=20) AppendFloat/Exp-16 249.1n ± 3% 252.5n ± 1% ~ (p=0.005 n=20) AppendFloat/NegExp-16 248.7n ± 3% 253.8n ± 2% ~ (p=0.006 n=20) AppendFloat/LongExp-16 258.4n ± 2% 253.0n ± 6% ~ (p=0.185 n=20) AppendFloat/Big-16 285.6n ± 1% 279.2n ± 5% ~ (p=0.012 n=20) AppendFloat/BinaryExp-16 89.47n ± 1% 91.85n ± 2% +2.65% (p=0.000 n=20) AppendFloat/32Integer-16 133.5n ± 1% 129.9n ± 1% ~ (p=0.004 n=20) AppendFloat/32ExactFraction-16 213.7n ± 1% 212.2n ± 2% ~ (p=0.071 n=20) AppendFloat/32Point-16 202.0n ± 0% 200.4n ± 1% ~ (p=0.223 n=20) AppendFloat/32Exp-16 236.4n ± 1% 239.8n ± 1% +1.44% (p=0.000 n=20) AppendFloat/32NegExp-16 212.5n ± 1% 211.9n ± 1% ~ (p=0.995 n=20) AppendFloat/32Shortest-16 200.3n ± 1% 193.9n ± 1% -3.20% (p=0.000 n=20) AppendFloat/32Fixed8Hard-16 136.0n ± 1% 133.2n ± 4% ~ (p=0.323 n=20) AppendFloat/32Fixed9Hard-16 155.6n ± 1% 156.7n ± 2% ~ (p=0.022 n=20) AppendFloat/64Fixed1-16 132.8n ± 1% 133.0n ± 3% ~ (p=0.199 n=20) AppendFloat/64Fixed2-16 128.9n ± 1% 129.7n ± 3% ~ (p=0.018 n=20) AppendFloat/64Fixed2.5-16 127.0n ± 1% 126.5n ± 3% ~ (p=0.825 n=20) AppendFloat/64Fixed3-16 127.3n ± 1% 130.3n ± 4% +2.32% (p=0.001 n=20) AppendFloat/64Fixed4-16 121.4n ± 1% 123.2n ± 2% +1.48% (p=0.000 n=20) AppendFloat/64Fixed5Hard-16 136.2n ± 1% 136.2n ± 3% ~ (p=0.256 n=20) AppendFloat/64Fixed12-16 159.0n ± 1% 165.2n ± 2% +3.93% (p=0.000 n=20) AppendFloat/64Fixed16-16 151.4n ± 0% 156.9n ± 1% +3.67% (p=0.000 n=20) AppendFloat/64Fixed12Hard-16 146.5n ± 1% 153.8n ± 1% +4.98% (p=0.000 n=20) AppendFloat/64Fixed17Hard-16 166.3n ± 1% 169.8n ± 1% +2.07% (p=0.001 n=20) AppendFloat/64Fixed18Hard-16 10.59µ ± 2% 10.60µ ± 0% ~ (p=0.499 n=20) AppendFloat/64FixedF1-16 614.4n ± 1% 152.0n ± 1% -75.27% (p=0.000 n=20) AppendFloat/64FixedF2-16 845.0n ± 0% 139.1n ± 1% -83.53% (p=0.000 n=20) AppendFloat/64FixedF3-16 608.8n ± 1% 129.3n ± 1% -78.77% (p=0.000 n=20) AppendFloat/Slowpath64-16 251.7n ± 1% 245.0n ± 1% -2.66% (p=0.000 n=20) AppendFloat/SlowpathDenormal64-16 248.4n ± 1% 241.2n ± 1% -2.90% (p=0.000 n=20) geomean 225.7n 193.8n -14.14% host: s7:GOARCH=386 cpu: AMD Ryzen 9 7950X 16-Core Processor │ 1cc918cc725 │ b66c604f523 │ │ sec/op │ sec/op vs base │ AppendFloat/Decimal-32 41.88n ± 0% 42.02n ± 1% ~ (p=0.004 n=20) AppendFloat/Float-32 71.05n ± 0% 71.24n ± 0% ~ (p=0.044 n=20) AppendFloat/Exp-32 74.91n ± 1% 74.80n ± 0% ~ (p=0.433 n=20) AppendFloat/NegExp-32 74.10n ± 0% 74.20n ± 0% ~ (p=0.867 n=20) AppendFloat/LongExp-32 75.73n ± 0% 75.84n ± 0% ~ (p=0.147 n=20) AppendFloat/Big-32 82.47n ± 0% 82.36n ± 0% ~ (p=0.490 n=20) AppendFloat/BinaryExp-32 32.31n ± 1% 32.62n ± 0% +0.97% (p=0.000 n=20) AppendFloat/32Integer-32 41.38n ± 1% 41.40n ± 1% ~ (p=0.106 n=20) AppendFloat/32ExactFraction-32 62.72n ± 0% 62.92n ± 0% ~ (p=0.009 n=20) AppendFloat/32Point-32 60.36n ± 0% 60.33n ± 0% ~ (p=0.050 n=20) AppendFloat/32Exp-32 68.97n ± 0% 69.24n ± 0% +0.39% (p=0.000 n=20) AppendFloat/32NegExp-32 62.63n ± 0% 63.15n ± 0% +0.82% (p=0.000 n=20) AppendFloat/32Shortest-32 58.76n ± 0% 58.87n ± 0% ~ (p=0.053 n=20) AppendFloat/32Fixed8Hard-32 41.67n ± 1% 43.46n ± 1% +4.30% (p=0.000 n=20) AppendFloat/32Fixed9Hard-32 49.78n ± 1% 50.53n ± 1% +1.52% (p=0.000 n=20) AppendFloat/64Fixed1-32 41.15n ± 0% 42.95n ± 1% +4.36% (p=0.000 n=20) AppendFloat/64Fixed2-32 40.83n ± 1% 41.24n ± 1% +1.03% (p=0.000 n=20) AppendFloat/64Fixed2.5-32 39.42n ± 0% 40.47n ± 1% +2.66% (p=0.000 n=20) AppendFloat/64Fixed3-32 40.73n ± 1% 41.58n ± 1% +2.10% (p=0.000 n=20) AppendFloat/64Fixed4-32 38.68n ± 0% 39.29n ± 0% +1.58% (p=0.000 n=20) AppendFloat/64Fixed5Hard-32 42.88n ± 1% 43.62n ± 1% +1.73% (p=0.000 n=20) AppendFloat/64Fixed12-32 51.67n ± 1% 52.92n ± 1% +2.42% (p=0.000 n=20) AppendFloat/64Fixed16-32 49.15n ± 0% 50.30n ± 0% +2.33% (p=0.000 n=20) AppendFloat/64Fixed12Hard-32 48.51n ± 0% 48.81n ± 0% +0.62% (p=0.001 n=20) AppendFloat/64Fixed17Hard-32 54.62n ± 1% 55.60n ± 1% +1.79% (p=0.000 n=20) AppendFloat/64Fixed18Hard-32 3.979µ ± 1% 3.980µ ± 1% ~ (p=0.569 n=20) AppendFloat/64FixedF1-32 165.90n ± 1% 49.97n ± 0% -69.88% (p=0.000 n=20) AppendFloat/64FixedF2-32 225.50n ± 0% 45.02n ± 1% -80.04% (p=0.000 n=20) AppendFloat/64FixedF3-32 160.20n ± 1% 42.16n ± 1% -73.69% (p=0.000 n=20) AppendFloat/Slowpath64-32 75.55n ± 0% 75.23n ± 0% -0.44% (p=0.000 n=20) AppendFloat/SlowpathDenormal64-32 74.84n ± 0% 75.00n ± 0% ~ (p=0.268 n=20) geomean 69.22n 61.13n -11.69% Change-Id: I722d2e2621e74e32cb3fc34a2df5b16cc595715c Reviewed-on: https://go-review.googlesource.com/c/go/+/717183 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Alan Donovan <adonovan@google.com>
1 parent 6e165b4 commit dd839f1

File tree

3 files changed

+84
-23
lines changed

3 files changed

+84
-23
lines changed

src/internal/strconv/ftoa.go

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -146,28 +146,38 @@ func genericFtoa(dst []byte, val float64, fmt byte, prec, bitSize int) []byte {
146146
return formatDigits(dst, shortest, neg, digs, prec, fmt)
147147
}
148148

149-
// TODO figure out when we can use fast code for f
150-
if fmt != 'f' {
151-
// Fixed number of digits.
152-
digits := prec
153-
switch fmt {
154-
case 'e', 'E':
155-
digits++
156-
case 'g', 'G':
157-
if prec == 0 {
158-
prec = 1
159-
}
160-
digits = prec
161-
default:
162-
// Invalid mode.
163-
digits = 1
149+
// Fixed number of digits.
150+
digits := prec
151+
switch fmt {
152+
case 'f':
153+
// %f precision specifies digits after the decimal point.
154+
// Estimate an upper bound on the total number of digits needed.
155+
// ftoaFixed will shorten as needed according to prec.
156+
if exp >= 0 {
157+
digits = 1 + mulLog10_2(1+exp) + prec
158+
} else {
159+
digits = 1 + prec - mulLog10_2(-exp)
164160
}
165-
if digits <= 18 {
161+
case 'e', 'E':
162+
digits++
163+
case 'g', 'G':
164+
if prec == 0 {
165+
prec = 1
166+
}
167+
digits = prec
168+
default:
169+
// Invalid mode.
170+
digits = 1
171+
}
172+
if digits <= 18 {
173+
// digits <= 0 happens for %f on very small numbers
174+
// and means that we're guaranteed to print all zeros.
175+
if digits > 0 {
166176
var buf [24]byte
167177
digs.d = buf[:]
168-
fixedFtoa(&digs, mant, exp-int(flt.mantbits), digits)
169-
return formatDigits(dst, false, neg, digs, prec, fmt)
178+
fixedFtoa(&digs, mant, exp-int(flt.mantbits), digits, prec, fmt)
170179
}
180+
return formatDigits(dst, false, neg, digs, prec, fmt)
171181
}
172182

173183
return bigFtoa(dst, prec, fmt, neg, mant, exp, flt)

src/internal/strconv/ftoa_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,29 @@ var ftoatests = []ftoaTest{
4242
{2000000, 'g', -1, "2e+06"},
4343
{1e10, 'g', -1, "1e+10"},
4444

45+
// f conversion basic cases
46+
{12345, 'f', 2, "12345.00"},
47+
{1234.5, 'f', 2, "1234.50"},
48+
{123.45, 'f', 2, "123.45"},
49+
{12.345, 'f', 2, "12.35"},
50+
{1.2345, 'f', 2, "1.23"},
51+
{0.12345, 'f', 2, "0.12"},
52+
{0.12945, 'f', 2, "0.13"},
53+
{0.012345, 'f', 2, "0.01"},
54+
{0.015, 'f', 2, "0.01"},
55+
{0.016, 'f', 2, "0.02"},
56+
{0.0052345, 'f', 2, "0.01"},
57+
{0.0012345, 'f', 2, "0.00"},
58+
{0.00012345, 'f', 2, "0.00"},
59+
{0.000012345, 'f', 2, "0.00"},
60+
61+
{0.996644984, 'f', 6, "0.996645"},
62+
{0.996644984, 'f', 5, "0.99664"},
63+
{0.996644984, 'f', 4, "0.9966"},
64+
{0.996644984, 'f', 3, "0.997"},
65+
{0.996644984, 'f', 2, "1.00"},
66+
{0.996644984, 'f', 1, "1.0"},
67+
4568
// g conversion and zero suppression
4669
{400, 'g', 2, "4e+02"},
4770
{40, 'g', 2, "40"},

src/internal/strconv/ftoafixed.go

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ var uint64pow10 = [...]uint64{
1313

1414
// fixedFtoa formats a number of decimal digits of mant*(2^exp) into d,
1515
// where mant > 0 and 1 ≤ digits ≤ 18.
16-
func fixedFtoa(d *decimalSlice, mant uint64, exp, digits int) {
16+
// If fmt == 'f', digits is a conservative overestimate, and the final
17+
// number of digits is prec past the decimal point.
18+
func fixedFtoa(d *decimalSlice, mant uint64, exp, digits, prec int, fmt byte) {
1719
// The strategy here is to multiply (mant * 2^exp) by a power of 10
1820
// to make the resulting integer be the number of digits we want.
1921
//
@@ -133,6 +135,28 @@ func fixedFtoa(d *decimalSlice, mant uint64, exp, digits int) {
133135
d.dp++
134136
}
135137

138+
// If this is %.*f we may have overestimated the digits needed.
139+
// Now that we know where the decimal point is,
140+
// trim to the actual number of digits, which is d.dp+prec.
141+
if fmt == 'f' && digits != d.dp+prec {
142+
for digits > d.dp+prec {
143+
var r uint
144+
dm, r = dm/10, uint(dm%10)
145+
dt |= bool2uint(r != 0)
146+
digits--
147+
}
148+
149+
// Dropping those digits can create a new leftmost
150+
// non-zero digit, like if we are formatting %.1f and
151+
// convert 0.09 -> 0.1. Detect and adjust for that.
152+
if digits <= 0 {
153+
digits = 1
154+
d.dp++
155+
}
156+
157+
max = uint64pow10[digits] << 1
158+
}
159+
136160
// Round and shift away rounding bit.
137161
// We want to round up when
138162
// (a) the fractional part is > 0.5 (dm&1 != 0 and dt == 1)
@@ -148,9 +172,13 @@ func fixedFtoa(d *decimalSlice, mant uint64, exp, digits int) {
148172
}
149173

150174
// Format digits into d.
151-
formatBase10(d.d[:digits], dm)
152-
d.nd = digits
153-
for d.d[d.nd-1] == '0' {
154-
d.nd--
175+
if dm != 0 {
176+
if formatBase10(d.d[:digits], dm) != 0 {
177+
panic("formatBase10")
178+
}
179+
d.nd = digits
180+
for d.d[d.nd-1] == '0' {
181+
d.nd--
182+
}
155183
}
156184
}

0 commit comments

Comments
 (0)