本文详解如何使用 strconv.FormatFloat 将 Go 中的 float64 值(如经纬度)转换为指定精度、不含指数形式(如 "64.2345" 而非 "4e+01")的字符串,避免 JSON 构建时出现格式错误。
先说一个常见误区:很多人在 Go 里跟浮点数打交道时,都会遇到一个坑——输出科学计数法。比如明明想要 "64.2345",结果出来个 "6e+01",尤其在处理经纬度这类数据并嵌入 JSON 时,这简直让人抓狂。
问题出在哪儿呢?根源在于 strconv.FormatFloat 的格式参数和精度设置。
- 默认推荐的格式动词 'g',它的逻辑是“看菜下饭”:数值大了或小了,自动切换到指数形式。一旦绝对值 ≥ 1e7 或 < 1e−3,立马就变成科学计数法。
- 精度设成 1 更是雪上加霜——它控制的是有效数字位数,不是小数位数。64.2345 这种数,保留 1 位有效数字,自然就变成了 "6e+01"。
解决办法很简单:用格式动词 'f',它强制固定小数点表示,彻底禁用指数。精度设为 -1,让 Go 自己选一个最简且无损的十进制形式——不丢数据、不补零、不搞花活。bitSize 明确为 64,对应 float64,保证解析一致性。
来看一段标准代码:
package main
import (
"fmt"
"strconv"
)
func main() {
// 模拟你的四个经纬度变量
startLat, startLon := 40.7128, -74.0060
endLat, endLon := 34.0522, -118.2437
// ✅ 正确转换:'f' + 精度 -1
sLat := strconv.FormatFloat(startLat, 'f', -1, 64)
sLon := strconv.FormatFloat(startLon, 'f', -1, 64)
eLat := strconv.FormatFloat(endLat, 'f', -1, 64)
eLon := strconv.FormatFloat(endLon, 'f', -1, 64)
// 安全构建 JSON 字符串(注意引号转义)
etaString := fmt.Sprintf(`{"start_latitude":"%s","start_longitude":"%s","end_latitude":"%s","end_longitude":"%s}`,
sLat, sLon, eLat, eLon)
fmt.Println(etaString)
// 输出:{"start_latitude":"40.7128","start_longitude":"-74.006","end_latitude":"34.0522","end_longitude":"-118.2437"}
}
这里必须强调几个关键点:
- 别试图拼接 []byte 和 float64:原始代码里那种 []byte(... + startLat + ...) 的写法,Go 根本不认。老老实实用 fmt.Sprintf 或 strings.Builder。
- JSON 安全性的取舍:如果浮点值里真的出现极罕见的特殊字符,或者你需要严格遵循 JSON 规范,那不如直接用 json.Marshal 构造结构体,手动拼接字符串反而容易出幺蛾子。
- 精度上的平衡艺术:-1 精度能保证 strconv.ParseFloat(s, 64) 反向解析后值完全一致,但代价是可能会输出很长的小数位——比如 0.1 变成 "0.10000000000000000555"。如果你需要固定小数位,比如保留 6 位,那就在 'f' 格式下显式设 prec = 6,例如 strconv.FormatFloat(x, 'f', 6, 64)。
- 性能的小提示:高频场景下,strings.Builder 比多次内存分配要高效不少,值得优先考虑。
说到底,用 strconv.FormatFloat(f, 'f', -1, 64) 是兼顾精度、可读性与 JSON 兼容性的最佳实践——它让 40.0 输出 "40",64.2345 输出 "64.2345",绝不会变成 "4e+01"。这才是最稳妥、最干净的做法。