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 }