1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package strconv_test 6 7 import ( 8 "math" 9 "math/rand" 10 . "strconv" 11 "testing" 12 ) 13 14 type ftoaTest struct { 15 f float64 16 fmt byte 17 prec int 18 s string 19 } 20 21 func fdiv(a, b float64) float64 { return a / b } 22 23 const ( 24 below1e23 = 99999999999999974834176 25 above1e23 = 100000000000000008388608 26 ) 27 28 var ftoatests = []ftoaTest{ 29 {1, 'e', 5, "1.00000e+00"}, 30 {1, 'f', 5, "1.00000"}, 31 {1, 'g', 5, "1"}, 32 {1, 'g', -1, "1"}, 33 {20, 'g', -1, "20"}, 34 {1234567.8, 'g', -1, "1.2345678e+06"}, 35 {200000, 'g', -1, "200000"}, 36 {2000000, 'g', -1, "2e+06"}, 37 38 // g conversion and zero suppression 39 {400, 'g', 2, "4e+02"}, 40 {40, 'g', 2, "40"}, 41 {4, 'g', 2, "4"}, 42 {.4, 'g', 2, "0.4"}, 43 {.04, 'g', 2, "0.04"}, 44 {.004, 'g', 2, "0.004"}, 45 {.0004, 'g', 2, "0.0004"}, 46 {.00004, 'g', 2, "4e-05"}, 47 {.000004, 'g', 2, "4e-06"}, 48 49 {0, 'e', 5, "0.00000e+00"}, 50 {0, 'f', 5, "0.00000"}, 51 {0, 'g', 5, "0"}, 52 {0, 'g', -1, "0"}, 53 54 {-1, 'e', 5, "-1.00000e+00"}, 55 {-1, 'f', 5, "-1.00000"}, 56 {-1, 'g', 5, "-1"}, 57 {-1, 'g', -1, "-1"}, 58 59 {12, 'e', 5, "1.20000e+01"}, 60 {12, 'f', 5, "12.00000"}, 61 {12, 'g', 5, "12"}, 62 {12, 'g', -1, "12"}, 63 64 {123456700, 'e', 5, "1.23457e+08"}, 65 {123456700, 'f', 5, "123456700.00000"}, 66 {123456700, 'g', 5, "1.2346e+08"}, 67 {123456700, 'g', -1, "1.234567e+08"}, 68 69 {1.2345e6, 'e', 5, "1.23450e+06"}, 70 {1.2345e6, 'f', 5, "1234500.00000"}, 71 {1.2345e6, 'g', 5, "1.2345e+06"}, 72 73 {1e23, 'e', 17, "9.99999999999999916e+22"}, 74 {1e23, 'f', 17, "99999999999999991611392.00000000000000000"}, 75 {1e23, 'g', 17, "9.9999999999999992e+22"}, 76 77 {1e23, 'e', -1, "1e+23"}, 78 {1e23, 'f', -1, "100000000000000000000000"}, 79 {1e23, 'g', -1, "1e+23"}, 80 81 {below1e23, 'e', 17, "9.99999999999999748e+22"}, 82 {below1e23, 'f', 17, "99999999999999974834176.00000000000000000"}, 83 {below1e23, 'g', 17, "9.9999999999999975e+22"}, 84 85 {below1e23, 'e', -1, "9.999999999999997e+22"}, 86 {below1e23, 'f', -1, "99999999999999970000000"}, 87 {below1e23, 'g', -1, "9.999999999999997e+22"}, 88 89 {above1e23, 'e', 17, "1.00000000000000008e+23"}, 90 {above1e23, 'f', 17, "100000000000000008388608.00000000000000000"}, 91 {above1e23, 'g', 17, "1.0000000000000001e+23"}, 92 93 {above1e23, 'e', -1, "1.0000000000000001e+23"}, 94 {above1e23, 'f', -1, "100000000000000010000000"}, 95 {above1e23, 'g', -1, "1.0000000000000001e+23"}, 96 97 {fdiv(5e-304, 1e20), 'g', -1, "5e-324"}, // avoid constant arithmetic 98 {fdiv(-5e-304, 1e20), 'g', -1, "-5e-324"}, // avoid constant arithmetic 99 100 {32, 'g', -1, "32"}, 101 {32, 'g', 0, "3e+01"}, 102 103 {100, 'x', -1, "%x"}, 104 105 {math.NaN(), 'g', -1, "NaN"}, 106 {-math.NaN(), 'g', -1, "NaN"}, 107 {math.Inf(0), 'g', -1, "+Inf"}, 108 {math.Inf(-1), 'g', -1, "-Inf"}, 109 {-math.Inf(0), 'g', -1, "-Inf"}, 110 111 {-1, 'b', -1, "-4503599627370496p-52"}, 112 113 // fixed bugs 114 {0.9, 'f', 1, "0.9"}, 115 {0.09, 'f', 1, "0.1"}, 116 {0.0999, 'f', 1, "0.1"}, 117 {0.05, 'f', 1, "0.1"}, 118 {0.05, 'f', 0, "0"}, 119 {0.5, 'f', 1, "0.5"}, 120 {0.5, 'f', 0, "0"}, 121 {1.5, 'f', 0, "2"}, 122 123 // http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/ 124 {2.2250738585072012e-308, 'g', -1, "2.2250738585072014e-308"}, 125 // http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/ 126 {2.2250738585072011e-308, 'g', -1, "2.225073858507201e-308"}, 127 128 // Issue 2625. 129 {383260575764816448, 'f', 0, "383260575764816448"}, 130 {383260575764816448, 'g', -1, "3.8326057576481645e+17"}, 131 } 132 133 func TestFtoa(t *testing.T) { 134 for i := 0; i < len(ftoatests); i++ { 135 test := &ftoatests[i] 136 s := FormatFloat(test.f, test.fmt, test.prec, 64) 137 if s != test.s { 138 t.Error("testN=64", test.f, string(test.fmt), test.prec, "want", test.s, "got", s) 139 } 140 x := AppendFloat([]byte("abc"), test.f, test.fmt, test.prec, 64) 141 if string(x) != "abc"+test.s { 142 t.Error("AppendFloat testN=64", test.f, string(test.fmt), test.prec, "want", "abc"+test.s, "got", string(x)) 143 } 144 if float64(float32(test.f)) == test.f && test.fmt != 'b' { 145 s := FormatFloat(test.f, test.fmt, test.prec, 32) 146 if s != test.s { 147 t.Error("testN=32", test.f, string(test.fmt), test.prec, "want", test.s, "got", s) 148 } 149 x := AppendFloat([]byte("abc"), test.f, test.fmt, test.prec, 32) 150 if string(x) != "abc"+test.s { 151 t.Error("AppendFloat testN=32", test.f, string(test.fmt), test.prec, "want", "abc"+test.s, "got", string(x)) 152 } 153 } 154 } 155 } 156 157 func TestFtoaRandom(t *testing.T) { 158 N := int(1e4) 159 if testing.Short() { 160 N = 100 161 } 162 t.Logf("testing %d random numbers with fast and slow FormatFloat", N) 163 for i := 0; i < N; i++ { 164 bits := uint64(rand.Uint32())<<32 | uint64(rand.Uint32()) 165 x := math.Float64frombits(bits) 166 167 shortFast := FormatFloat(x, 'g', -1, 64) 168 SetOptimize(false) 169 shortSlow := FormatFloat(x, 'g', -1, 64) 170 SetOptimize(true) 171 if shortSlow != shortFast { 172 t.Errorf("%b printed as %s, want %s", x, shortFast, shortSlow) 173 } 174 175 prec := rand.Intn(12) + 5 176 shortFast = FormatFloat(x, 'e', prec, 64) 177 SetOptimize(false) 178 shortSlow = FormatFloat(x, 'e', prec, 64) 179 SetOptimize(true) 180 if shortSlow != shortFast { 181 t.Errorf("%b printed as %s, want %s", x, shortFast, shortSlow) 182 } 183 } 184 } 185 186 var ftoaBenches = []struct { 187 name string 188 float float64 189 fmt byte 190 prec int 191 bitSize int 192 }{ 193 {"Decimal", 33909, 'g', -1, 64}, 194 {"Float", 339.7784, 'g', -1, 64}, 195 {"Exp", -5.09e75, 'g', -1, 64}, 196 {"NegExp", -5.11e-95, 'g', -1, 64}, 197 198 {"Big", 123456789123456789123456789, 'g', -1, 64}, 199 {"BinaryExp", -1, 'b', -1, 64}, 200 201 {"32Integer", 33909, 'g', -1, 32}, 202 {"32ExactFraction", 3.375, 'g', -1, 32}, 203 {"32Point", 339.7784, 'g', -1, 32}, 204 {"32Exp", -5.09e25, 'g', -1, 32}, 205 {"32NegExp", -5.11e-25, 'g', -1, 32}, 206 207 {"64Fixed1", 123456, 'e', 3, 64}, 208 {"64Fixed2", 123.456, 'e', 3, 64}, 209 {"64Fixed3", 1.23456e+78, 'e', 3, 64}, 210 {"64Fixed4", 1.23456e-78, 'e', 3, 64}, 211 212 // Trigger slow path (see issue #15672). 213 {"Slowpath64", 622666234635.3213e-320, 'e', -1, 64}, 214 } 215 216 func BenchmarkFormatFloat(b *testing.B) { 217 for _, c := range ftoaBenches { 218 b.Run(c.name, func(b *testing.B) { 219 for i := 0; i < b.N; i++ { 220 FormatFloat(c.float, c.fmt, c.prec, c.bitSize) 221 } 222 }) 223 } 224 } 225 226 func BenchmarkAppendFloat(b *testing.B) { 227 dst := make([]byte, 30) 228 for _, c := range ftoaBenches { 229 b.Run(c.name, func(b *testing.B) { 230 for i := 0; i < b.N; i++ { 231 AppendFloat(dst[:0], c.float, c.fmt, c.prec, c.bitSize) 232 } 233 }) 234 } 235 } 236