github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/vendor_skip/golang.org/x/text/internal/number/decimal.go (about) 1 // Copyright 2017 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:generate stringer -type RoundingMode 6 7 package number 8 9 import ( 10 "math" 11 "strconv" 12 ) 13 14 // RoundingMode determines how a number is rounded to the desired precision. 15 type RoundingMode byte 16 17 const ( 18 ToNearestEven RoundingMode = iota // towards the nearest integer, or towards an even number if equidistant. 19 ToNearestZero // towards the nearest integer, or towards zero if equidistant. 20 ToNearestAway // towards the nearest integer, or away from zero if equidistant. 21 ToPositiveInf // towards infinity 22 ToNegativeInf // towards negative infinity 23 ToZero // towards zero 24 AwayFromZero // away from zero 25 numModes 26 ) 27 28 const maxIntDigits = 20 29 30 // A Decimal represents a floating point number in decimal format. 31 // Digits represents a number [0, 1.0), and the absolute value represented by 32 // Decimal is Digits * 10^Exp. Leading and trailing zeros may be omitted and Exp 33 // may point outside a valid position in Digits. 34 // 35 // Examples: 36 // 37 // Number Decimal 38 // 12345 Digits: [1, 2, 3, 4, 5], Exp: 5 39 // 12.345 Digits: [1, 2, 3, 4, 5], Exp: 2 40 // 12000 Digits: [1, 2], Exp: 5 41 // 12000.00 Digits: [1, 2], Exp: 5 42 // 0.00123 Digits: [1, 2, 3], Exp: -2 43 // 0 Digits: [], Exp: 0 44 type Decimal struct { 45 digits 46 47 buf [maxIntDigits]byte 48 } 49 50 type digits struct { 51 Digits []byte // mantissa digits, big-endian 52 Exp int32 // exponent 53 Neg bool 54 Inf bool // Takes precedence over Digits and Exp. 55 NaN bool // Takes precedence over Inf. 56 } 57 58 // Digits represents a floating point number represented in digits of the 59 // base in which a number is to be displayed. It is similar to Decimal, but 60 // keeps track of trailing fraction zeros and the comma placement for 61 // engineering notation. Digits must have at least one digit. 62 // 63 // Examples: 64 // 65 // Number Decimal 66 // decimal 67 // 12345 Digits: [1, 2, 3, 4, 5], Exp: 5 End: 5 68 // 12.345 Digits: [1, 2, 3, 4, 5], Exp: 2 End: 5 69 // 12000 Digits: [1, 2], Exp: 5 End: 5 70 // 12000.00 Digits: [1, 2], Exp: 5 End: 7 71 // 0.00123 Digits: [1, 2, 3], Exp: -2 End: 3 72 // 0 Digits: [], Exp: 0 End: 1 73 // scientific (actual exp is Exp - Comma) 74 // 0e0 Digits: [0], Exp: 1, End: 1, Comma: 1 75 // .0e0 Digits: [0], Exp: 0, End: 1, Comma: 0 76 // 0.0e0 Digits: [0], Exp: 1, End: 2, Comma: 1 77 // 1.23e4 Digits: [1, 2, 3], Exp: 5, End: 3, Comma: 1 78 // .123e5 Digits: [1, 2, 3], Exp: 5, End: 3, Comma: 0 79 // engineering 80 // 12.3e3 Digits: [1, 2, 3], Exp: 5, End: 3, Comma: 2 81 type Digits struct { 82 digits 83 // End indicates the end position of the number. 84 End int32 // For decimals Exp <= End. For scientific len(Digits) <= End. 85 // Comma is used for the comma position for scientific (always 0 or 1) and 86 // engineering notation (always 0, 1, 2, or 3). 87 Comma uint8 88 // IsScientific indicates whether this number is to be rendered as a 89 // scientific number. 90 IsScientific bool 91 } 92 93 func (d *Digits) NumFracDigits() int { 94 if d.Exp >= d.End { 95 return 0 96 } 97 return int(d.End - d.Exp) 98 } 99 100 // normalize returns a new Decimal with leading and trailing zeros removed. 101 func (d *Decimal) normalize() (n Decimal) { 102 n = *d 103 b := n.Digits 104 // Strip leading zeros. Resulting number of digits is significant digits. 105 for len(b) > 0 && b[0] == 0 { 106 b = b[1:] 107 n.Exp-- 108 } 109 // Strip trailing zeros 110 for len(b) > 0 && b[len(b)-1] == 0 { 111 b = b[:len(b)-1] 112 } 113 if len(b) == 0 { 114 n.Exp = 0 115 } 116 n.Digits = b 117 return n 118 } 119 120 func (d *Decimal) clear() { 121 b := d.Digits 122 if b == nil { 123 b = d.buf[:0] 124 } 125 *d = Decimal{} 126 d.Digits = b[:0] 127 } 128 129 func (x *Decimal) String() string { 130 if x.NaN { 131 return "NaN" 132 } 133 var buf []byte 134 if x.Neg { 135 buf = append(buf, '-') 136 } 137 if x.Inf { 138 buf = append(buf, "Inf"...) 139 return string(buf) 140 } 141 switch { 142 case len(x.Digits) == 0: 143 buf = append(buf, '0') 144 case x.Exp <= 0: 145 // 0.00ddd 146 buf = append(buf, "0."...) 147 buf = appendZeros(buf, -int(x.Exp)) 148 buf = appendDigits(buf, x.Digits) 149 150 case /* 0 < */ int(x.Exp) < len(x.Digits): 151 // dd.ddd 152 buf = appendDigits(buf, x.Digits[:x.Exp]) 153 buf = append(buf, '.') 154 buf = appendDigits(buf, x.Digits[x.Exp:]) 155 156 default: // len(x.Digits) <= x.Exp 157 // ddd00 158 buf = appendDigits(buf, x.Digits) 159 buf = appendZeros(buf, int(x.Exp)-len(x.Digits)) 160 } 161 return string(buf) 162 } 163 164 func appendDigits(buf []byte, digits []byte) []byte { 165 for _, c := range digits { 166 buf = append(buf, c+'0') 167 } 168 return buf 169 } 170 171 // appendZeros appends n 0 digits to buf and returns buf. 172 func appendZeros(buf []byte, n int) []byte { 173 for ; n > 0; n-- { 174 buf = append(buf, '0') 175 } 176 return buf 177 } 178 179 func (d *digits) round(mode RoundingMode, n int) { 180 if n >= len(d.Digits) { 181 return 182 } 183 // Make rounding decision: The result mantissa is truncated ("rounded down") 184 // by default. Decide if we need to increment, or "round up", the (unsigned) 185 // mantissa. 186 inc := false 187 switch mode { 188 case ToNegativeInf: 189 inc = d.Neg 190 case ToPositiveInf: 191 inc = !d.Neg 192 case ToZero: 193 // nothing to do 194 case AwayFromZero: 195 inc = true 196 case ToNearestEven: 197 inc = d.Digits[n] > 5 || d.Digits[n] == 5 && 198 (len(d.Digits) > n+1 || n == 0 || d.Digits[n-1]&1 != 0) 199 case ToNearestAway: 200 inc = d.Digits[n] >= 5 201 case ToNearestZero: 202 inc = d.Digits[n] > 5 || d.Digits[n] == 5 && len(d.Digits) > n+1 203 default: 204 panic("unreachable") 205 } 206 if inc { 207 d.roundUp(n) 208 } else { 209 d.roundDown(n) 210 } 211 } 212 213 // roundFloat rounds a floating point number. 214 func (r RoundingMode) roundFloat(x float64) float64 { 215 // Make rounding decision: The result mantissa is truncated ("rounded down") 216 // by default. Decide if we need to increment, or "round up", the (unsigned) 217 // mantissa. 218 abs := x 219 if x < 0 { 220 abs = -x 221 } 222 i, f := math.Modf(abs) 223 if f == 0.0 { 224 return x 225 } 226 inc := false 227 switch r { 228 case ToNegativeInf: 229 inc = x < 0 230 case ToPositiveInf: 231 inc = x >= 0 232 case ToZero: 233 // nothing to do 234 case AwayFromZero: 235 inc = true 236 case ToNearestEven: 237 // TODO: check overflow 238 inc = f > 0.5 || f == 0.5 && int64(i)&1 != 0 239 case ToNearestAway: 240 inc = f >= 0.5 241 case ToNearestZero: 242 inc = f > 0.5 243 default: 244 panic("unreachable") 245 } 246 if inc { 247 i += 1 248 } 249 if abs != x { 250 i = -i 251 } 252 return i 253 } 254 255 func (x *digits) roundUp(n int) { 256 if n < 0 || n >= len(x.Digits) { 257 return // nothing to do 258 } 259 // find first digit < 9 260 for n > 0 && x.Digits[n-1] >= 9 { 261 n-- 262 } 263 264 if n == 0 { 265 // all digits are 9s => round up to 1 and update exponent 266 x.Digits[0] = 1 // ok since len(x.Digits) > n 267 x.Digits = x.Digits[:1] 268 x.Exp++ 269 return 270 } 271 x.Digits[n-1]++ 272 x.Digits = x.Digits[:n] 273 // x already trimmed 274 } 275 276 func (x *digits) roundDown(n int) { 277 if n < 0 || n >= len(x.Digits) { 278 return // nothing to do 279 } 280 x.Digits = x.Digits[:n] 281 trim(x) 282 } 283 284 // trim cuts off any trailing zeros from x's mantissa; 285 // they are meaningless for the value of x. 286 func trim(x *digits) { 287 i := len(x.Digits) 288 for i > 0 && x.Digits[i-1] == 0 { 289 i-- 290 } 291 x.Digits = x.Digits[:i] 292 if i == 0 { 293 x.Exp = 0 294 } 295 } 296 297 // A Converter converts a number into decimals according to the given rounding 298 // criteria. 299 type Converter interface { 300 Convert(d *Decimal, r RoundingContext) 301 } 302 303 const ( 304 signed = true 305 unsigned = false 306 ) 307 308 // Convert converts the given number to the decimal representation using the 309 // supplied RoundingContext. 310 func (d *Decimal) Convert(r RoundingContext, number interface{}) { 311 switch f := number.(type) { 312 case Converter: 313 d.clear() 314 f.Convert(d, r) 315 case float32: 316 d.ConvertFloat(r, float64(f), 32) 317 case float64: 318 d.ConvertFloat(r, f, 64) 319 case int: 320 d.ConvertInt(r, signed, uint64(f)) 321 case int8: 322 d.ConvertInt(r, signed, uint64(f)) 323 case int16: 324 d.ConvertInt(r, signed, uint64(f)) 325 case int32: 326 d.ConvertInt(r, signed, uint64(f)) 327 case int64: 328 d.ConvertInt(r, signed, uint64(f)) 329 case uint: 330 d.ConvertInt(r, unsigned, uint64(f)) 331 case uint8: 332 d.ConvertInt(r, unsigned, uint64(f)) 333 case uint16: 334 d.ConvertInt(r, unsigned, uint64(f)) 335 case uint32: 336 d.ConvertInt(r, unsigned, uint64(f)) 337 case uint64: 338 d.ConvertInt(r, unsigned, f) 339 340 default: 341 d.NaN = true 342 // TODO: 343 // case string: if produced by strconv, allows for easy arbitrary pos. 344 // case reflect.Value: 345 // case big.Float 346 // case big.Int 347 // case big.Rat? 348 // catch underlyings using reflect or will this already be done by the 349 // message package? 350 } 351 } 352 353 // ConvertInt converts an integer to decimals. 354 func (d *Decimal) ConvertInt(r RoundingContext, signed bool, x uint64) { 355 if r.Increment > 0 { 356 // TODO: if uint64 is too large, fall back to float64 357 if signed { 358 d.ConvertFloat(r, float64(int64(x)), 64) 359 } else { 360 d.ConvertFloat(r, float64(x), 64) 361 } 362 return 363 } 364 d.clear() 365 if signed && int64(x) < 0 { 366 x = uint64(-int64(x)) 367 d.Neg = true 368 } 369 d.fillIntDigits(x) 370 d.Exp = int32(len(d.Digits)) 371 } 372 373 // ConvertFloat converts a floating point number to decimals. 374 func (d *Decimal) ConvertFloat(r RoundingContext, x float64, size int) { 375 d.clear() 376 if math.IsNaN(x) { 377 d.NaN = true 378 return 379 } 380 // Simple case: decimal notation 381 if r.Increment > 0 { 382 scale := int(r.IncrementScale) 383 mult := 1.0 384 if scale >= len(scales) { 385 mult = math.Pow(10, float64(scale)) 386 } else { 387 mult = scales[scale] 388 } 389 // We multiply x instead of dividing inc as it gives less rounding 390 // issues. 391 x *= mult 392 x /= float64(r.Increment) 393 x = r.Mode.roundFloat(x) 394 x *= float64(r.Increment) 395 x /= mult 396 } 397 398 abs := x 399 if x < 0 { 400 d.Neg = true 401 abs = -x 402 } 403 if math.IsInf(abs, 1) { 404 d.Inf = true 405 return 406 } 407 408 // By default we get the exact decimal representation. 409 verb := byte('g') 410 prec := -1 411 // As the strconv API does not return the rounding accuracy, we can only 412 // round using ToNearestEven. 413 if r.Mode == ToNearestEven { 414 if n := r.RoundSignificantDigits(); n >= 0 { 415 prec = n 416 } else if n = r.RoundFractionDigits(); n >= 0 { 417 prec = n 418 verb = 'f' 419 } 420 } else { 421 // TODO: At this point strconv's rounding is imprecise to the point that 422 // it is not usable for this purpose. 423 // See https://github.com/golang/go/issues/21714 424 // If rounding is requested, we ask for a large number of digits and 425 // round from there to simulate rounding only once. 426 // Ideally we would have strconv export an AppendDigits that would take 427 // a rounding mode and/or return an accuracy. Something like this would 428 // work: 429 // AppendDigits(dst []byte, x float64, base, size, prec int) (digits []byte, exp, accuracy int) 430 hasPrec := r.RoundSignificantDigits() >= 0 431 hasScale := r.RoundFractionDigits() >= 0 432 if hasPrec || hasScale { 433 // prec is the number of mantissa bits plus some extra for safety. 434 // We need at least the number of mantissa bits as decimals to 435 // accurately represent the floating point without rounding, as each 436 // bit requires one more decimal to represent: 0.5, 0.25, 0.125, ... 437 prec = 60 438 } 439 } 440 441 b := strconv.AppendFloat(d.Digits[:0], abs, verb, prec, size) 442 i := 0 443 k := 0 444 beforeDot := 1 445 for i < len(b) { 446 if c := b[i]; '0' <= c && c <= '9' { 447 b[k] = c - '0' 448 k++ 449 d.Exp += int32(beforeDot) 450 } else if c == '.' { 451 beforeDot = 0 452 d.Exp = int32(k) 453 } else { 454 break 455 } 456 i++ 457 } 458 d.Digits = b[:k] 459 if i != len(b) { 460 i += len("e") 461 pSign := i 462 exp := 0 463 for i++; i < len(b); i++ { 464 exp *= 10 465 exp += int(b[i] - '0') 466 } 467 if b[pSign] == '-' { 468 exp = -exp 469 } 470 d.Exp = int32(exp) + 1 471 } 472 } 473 474 func (d *Decimal) fillIntDigits(x uint64) { 475 if cap(d.Digits) < maxIntDigits { 476 d.Digits = d.buf[:] 477 } else { 478 d.Digits = d.buf[:maxIntDigits] 479 } 480 i := 0 481 for ; x > 0; x /= 10 { 482 d.Digits[i] = byte(x % 10) 483 i++ 484 } 485 d.Digits = d.Digits[:i] 486 for p := 0; p < i; p++ { 487 i-- 488 d.Digits[p], d.Digits[i] = d.Digits[i], d.Digits[p] 489 } 490 } 491 492 var scales [70]float64 493 494 func init() { 495 x := 1.0 496 for i := range scales { 497 scales[i] = x 498 x *= 10 499 } 500 }