github.com/v2pro/plz@v0.0.0-20221028024117-e5f9aec5b631/msgfmt/jsonfmt/encoder_float.go (about) 1 package jsonfmt 2 3 import ( 4 "unsafe" 5 "math" 6 "strconv" 7 "context" 8 ) 9 10 var pow10 []uint64 11 12 func init() { 13 pow10 = []uint64{1, 10, 100, 1000, 10000, 100000, 1000000} 14 } 15 16 type lossyFloat64Encoder struct { 17 } 18 19 func (encoder *lossyFloat64Encoder) Encode(ctx context.Context, space []byte, ptr unsafe.Pointer) []byte { 20 return WriteFloat64Lossy(space, *(*float64)(ptr)) 21 } 22 23 type lossyFloat32Encoder struct { 24 } 25 26 func (encoder *lossyFloat32Encoder) Encode(ctx context.Context, space []byte, ptr unsafe.Pointer) []byte { 27 return WriteFloat32Lossy(space, *(*float32)(ptr)) 28 } 29 30 // WriteFloat64 write float64 to stream 31 func WriteFloat64(space []byte, val float64) []byte { 32 abs := math.Abs(val) 33 fmt := byte('f') 34 // Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right. 35 if abs != 0 { 36 if abs < 1e-6 || abs >= 1e21 { 37 fmt = 'e' 38 } 39 } 40 return append(space, strconv.FormatFloat(float64(val), fmt, -1, 64)...) 41 } 42 // WriteFloat64Lossy write float64 to stream with ONLY 6 digits precision although much much faster 43 func WriteFloat64Lossy(space []byte, val float64) []byte { 44 if val < 0 { 45 space = append(space, '-') 46 val = -val 47 } 48 if val > 0x4ffffff { 49 return WriteFloat64(space, val) 50 } 51 precision := 6 52 exp := uint64(1000000) // 6 53 lval := uint64(val*float64(exp) + 0.5) 54 space = WriteUint64(space, lval / exp) 55 fval := lval % exp 56 if fval == 0 { 57 return space 58 } 59 space = append(space, '.') 60 for p := precision - 1; p > 0 && fval < pow10[p]; p-- { 61 space = append(space, '0') 62 } 63 space = WriteUint64(space, fval) 64 for space[len(space)-1] == '0' { 65 space = space[:len(space) - 1] 66 } 67 return space 68 } 69 70 // WriteFloat32 write float32 to stream 71 func WriteFloat32(space []byte, val float32) []byte { 72 abs := math.Abs(float64(val)) 73 fmt := byte('f') 74 // Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right. 75 if abs != 0 { 76 if float32(abs) < 1e-6 || float32(abs) >= 1e21 { 77 fmt = 'e' 78 } 79 } 80 return append(space, strconv.FormatFloat(float64(val), fmt, -1, 32)...) 81 } 82 83 // WriteFloat32Lossy write float32 to stream with ONLY 6 digits precision although much much faster 84 func WriteFloat32Lossy(space []byte, val float32) []byte { 85 if val < 0 { 86 space = append(space, '-') 87 val = -val 88 } 89 if val > 0x4ffffff { 90 return WriteFloat32(space, val) 91 } 92 precision := 6 93 exp := uint64(1000000) // 6 94 lval := uint64(float64(val)*float64(exp) + 0.5) 95 space = WriteUint64(space, lval / exp) 96 fval := lval % exp 97 if fval == 0 { 98 return space 99 } 100 space = append(space, '.') 101 for p := precision - 1; p > 0 && fval < pow10[p]; p-- { 102 space = append(space, '0') 103 } 104 space = WriteUint64(space, fval) 105 for space[len(space)-1] == '0' { 106 space = space[:len(space) - 1] 107 } 108 return space 109 }