-
Notifications
You must be signed in to change notification settings - Fork 0
/
prefixes.go
115 lines (108 loc) · 1.97 KB
/
prefixes.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package unit
import (
"fmt"
"strconv"
"strings"
"unicode/utf8"
)
// SI prefixes.
const (
Kilo = 1e3 // k
Centi = 1e-2 // c
Milli = 1e-3 // m
Micro = 1e-6 // µ
Nano = 1e-9 // n
)
func getPrefix(v float64) float64 {
switch {
case v >= Kilo:
return Kilo
case v >= 0.1 || v == 0:
return 1
case v >= Centi:
return Centi
case v >= Milli:
return Milli
case v >= Micro:
return Micro
default:
return Nano
}
}
func formatPrefix(p float64) string {
switch p {
case Kilo:
return "k"
case Centi:
return "c"
case Milli:
return "m"
case Micro:
return "µ"
case Nano:
return "n"
default:
return ""
}
}
func parsePrefix(p rune) (float64, bool) {
switch p {
case 'k':
return Kilo, true
case 'c':
return Centi, true
case 'm':
return Milli, true
case 'µ':
return Micro, true
case 'n':
return Nano, true
default:
return 1, false
}
}
func parse(str string, units map[string]float64) (float64, error) {
rest := str
// parse unit
var okUnit bool
var symbol string
var unit float64
for symbol, unit = range units {
if !strings.HasSuffix(rest, symbol) {
continue
}
okUnit = true
break
}
if !okUnit {
return 0, fmt.Errorf("parse '%s': unknown unit", str)
}
rest = rest[:len(rest)-len(symbol)]
if len(rest) == 0 {
return 0, fmt.Errorf("parse '%s': not a number", str)
}
// parse prefix, if any
lastRune, lastRuneSize := utf8.DecodeLastRuneInString(rest)
prefix, hasPrefix := parsePrefix(lastRune)
if hasPrefix {
rest = rest[:len(rest)-lastRuneSize]
}
if len(rest) == 0 {
return 0, fmt.Errorf("parse '%s': not a number", str)
}
// parse magnitude
magnitude, err := strconv.ParseFloat(rest, 64)
if err != nil {
return 0, fmt.Errorf("parse '%s': %w", str, err)
}
return magnitude * prefix * unit, nil
}
func format(v float64, symbol string) string {
var sign string
if v < 0 {
sign = "-"
v *= -1
}
prefix := getPrefix(v)
return sign + strconv.FormatFloat(v/prefix, 'g', -1, 64) + formatPrefix(prefix) + symbol
}