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  }