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