go.ketch.com/lib/goja@v0.0.1/ftoa/ftostr.go (about) 1 package ftoa 2 3 import ( 4 "math" 5 "strconv" 6 7 "go.ketch.com/lib/goja/ftoa/internal/fast" 8 ) 9 10 type FToStrMode int 11 12 const ( 13 // Either fixed or exponential format; round-trip 14 ModeStandard FToStrMode = iota 15 // Always exponential format; round-trip 16 ModeStandardExponential 17 // Round to <precision> digits after the decimal point; exponential if number is large 18 ModeFixed 19 // Always exponential format; <precision> significant digits 20 ModeExponential 21 // Either fixed or exponential format; <precision> significant digits 22 ModePrecision 23 ) 24 25 func insert(b []byte, p int, c byte) []byte { 26 b = append(b, 0) 27 copy(b[p+1:], b[p:]) 28 b[p] = c 29 return b 30 } 31 32 func expand(b []byte, delta int) []byte { 33 newLen := len(b) + delta 34 if newLen <= cap(b) { 35 return b[:newLen] 36 } 37 b1 := make([]byte, newLen) 38 copy(b1, b) 39 return b1 40 } 41 42 func FToStr(d float64, mode FToStrMode, precision int, buffer []byte) []byte { 43 if math.IsNaN(d) { 44 buffer = append(buffer, "NaN"...) 45 return buffer 46 } 47 if math.IsInf(d, 0) { 48 if math.Signbit(d) { 49 buffer = append(buffer, '-') 50 } 51 buffer = append(buffer, "Infinity"...) 52 return buffer 53 } 54 55 if mode == ModeFixed && (d >= 1e21 || d <= -1e21) { 56 mode = ModeStandard 57 } 58 59 var decPt int 60 var ok bool 61 startPos := len(buffer) 62 63 if d != 0 { // also matches -0 64 if d < 0 { 65 buffer = append(buffer, '-') 66 d = -d 67 startPos++ 68 } 69 switch mode { 70 case ModeStandard, ModeStandardExponential: 71 buffer, decPt, ok = fast.Dtoa(d, fast.ModeShortest, 0, buffer) 72 case ModeExponential, ModePrecision: 73 buffer, decPt, ok = fast.Dtoa(d, fast.ModePrecision, precision, buffer) 74 } 75 } else { 76 buffer = append(buffer, '0') 77 decPt, ok = 1, true 78 } 79 if !ok { 80 buffer, decPt = ftoa(d, dtoaModes[mode], mode >= ModeFixed, precision, buffer) 81 } 82 exponentialNotation := false 83 minNDigits := 0 /* Minimum number of significand digits required by mode and precision */ 84 nDigits := len(buffer) - startPos 85 86 switch mode { 87 case ModeStandard: 88 if decPt < -5 || decPt > 21 { 89 exponentialNotation = true 90 } else { 91 minNDigits = decPt 92 } 93 case ModeFixed: 94 if precision >= 0 { 95 minNDigits = decPt + precision 96 } else { 97 minNDigits = decPt 98 } 99 case ModeExponential: 100 // JS_ASSERT(precision > 0); 101 minNDigits = precision 102 fallthrough 103 case ModeStandardExponential: 104 exponentialNotation = true 105 case ModePrecision: 106 // JS_ASSERT(precision > 0); 107 minNDigits = precision 108 if decPt < -5 || decPt > precision { 109 exponentialNotation = true 110 } 111 } 112 113 for nDigits < minNDigits { 114 buffer = append(buffer, '0') 115 nDigits++ 116 } 117 118 if exponentialNotation { 119 /* Insert a decimal point if more than one significand digit */ 120 if nDigits != 1 { 121 buffer = insert(buffer, startPos+1, '.') 122 } 123 buffer = append(buffer, 'e') 124 if decPt-1 >= 0 { 125 buffer = append(buffer, '+') 126 } 127 buffer = strconv.AppendInt(buffer, int64(decPt-1), 10) 128 } else if decPt != nDigits { 129 /* Some kind of a fraction in fixed notation */ 130 // JS_ASSERT(decPt <= nDigits); 131 if decPt > 0 { 132 /* dd...dd . dd...dd */ 133 buffer = insert(buffer, startPos+decPt, '.') 134 } else { 135 /* 0 . 00...00dd...dd */ 136 buffer = expand(buffer, 2-decPt) 137 copy(buffer[startPos+2-decPt:], buffer[startPos:]) 138 buffer[startPos] = '0' 139 buffer[startPos+1] = '.' 140 for i := startPos + 2; i < startPos+2-decPt; i++ { 141 buffer[i] = '0' 142 } 143 } 144 } 145 146 return buffer 147 }