github.com/trim21/go-phpserialize@v0.0.22-0.20240301204449-2fca0319b3f0/internal/encoder/float.go (about)

     1  package encoder
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"reflect"
     7  	"strconv"
     8  	"unsafe"
     9  
    10  	"github.com/trim21/go-phpserialize/internal/runtime"
    11  )
    12  
    13  func encodeFloat32(ctx *Ctx, b []byte, p uintptr) ([]byte, error) {
    14  	value := **(**float32)(unsafe.Pointer(&p))
    15  	return appendFloat32(b, value), nil
    16  }
    17  
    18  func encodeFloat64(ctx *Ctx, b []byte, p uintptr) ([]byte, error) {
    19  	value := **(**float64)(unsafe.Pointer(&p))
    20  	return appendFloat64(b, value), nil
    21  }
    22  
    23  // https://github.com/goccy/go-json/blob/4d0a50640b999aeafd15e3b20d8ad47fe917e6e8/internal/encoder/encoder.go#L335
    24  
    25  func appendFloat32(b []byte, f32 float32) []byte {
    26  	f64 := float64(f32)
    27  	abs := math.Abs(f64)
    28  	format := byte('f')
    29  	// Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.
    30  	if abs != 0 {
    31  		f32 := float32(abs)
    32  		if f32 < 1e-6 || f32 >= 1e21 {
    33  			format = 'e'
    34  		}
    35  	}
    36  
    37  	b = append(b, 'd', ':')
    38  	b = strconv.AppendFloat(b, f64, format, -1, 32)
    39  	return append(b, ';')
    40  }
    41  
    42  func appendFloat64(b []byte, f64 float64) []byte {
    43  	abs := math.Abs(f64)
    44  	format := byte('f')
    45  	// Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.
    46  	if abs != 0 {
    47  		if abs < 1e-6 || abs >= 1e21 {
    48  			format = 'e'
    49  		}
    50  	}
    51  
    52  	b = append(b, 'd', ':')
    53  	b = strconv.AppendFloat(b, f64, format, -1, 64)
    54  	return append(b, ';')
    55  }
    56  
    57  func compileFloatAsString(typ *runtime.Type) (encoder, error) {
    58  	switch typ.Kind() {
    59  	case reflect.Float32:
    60  		return encodeFloat32AsString, nil
    61  	case reflect.Float64:
    62  		return encodeFloat64AsString, nil
    63  	}
    64  
    65  	panic(fmt.Sprintf("unexpected kind %s", typ.Kind()))
    66  }
    67  
    68  func encodeFloat32AsString(ctx *Ctx, b []byte, p uintptr) ([]byte, error) {
    69  	value := **(**float32)(unsafe.Pointer(&p))
    70  	return appendFloat32AsString(ctx.smallBuffer[:0], b, value), nil
    71  }
    72  
    73  func encodeFloat64AsString(ctx *Ctx, b []byte, p uintptr) ([]byte, error) {
    74  	f64 := **(**float64)(unsafe.Pointer(&p))
    75  	return appendFloat64AsString(ctx.smallBuffer[:0], b, f64), nil
    76  }
    77  
    78  func appendFloat32AsString(buf []byte, b []byte, f32 float32) []byte {
    79  	f64 := float64(f32)
    80  	abs := math.Abs(f64)
    81  	format := byte('f')
    82  	// Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.
    83  	if abs != 0 {
    84  		f32 := float32(abs)
    85  		if f32 < 1e-6 || f32 >= 1e21 {
    86  			format = 'e'
    87  		}
    88  	}
    89  
    90  	buf = strconv.AppendFloat(buf, f64, format, -1, 32)
    91  	b = appendStringHead(b, int64(len(buf)))
    92  	b = append(b, '"')
    93  	b = append(b, buf...)
    94  	b = append(b, '"', ';')
    95  
    96  	return b
    97  }
    98  
    99  func appendFloat64AsString(buf []byte, b []byte, f64 float64) []byte {
   100  	abs := math.Abs(f64)
   101  	format := byte('f')
   102  	// Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.
   103  	if abs != 0 {
   104  		if abs < 1e-6 || abs >= 1e21 {
   105  			format = 'e'
   106  		}
   107  	}
   108  
   109  	buf = strconv.AppendFloat(buf, f64, format, -1, 64)
   110  	b = appendStringHead(b, int64(len(buf)))
   111  	b = append(b, '"')
   112  	b = append(b, buf...)
   113  	b = append(b, '"', ';')
   114  
   115  	return b
   116  }