-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathladder.go
More file actions
65 lines (54 loc) · 1.36 KB
/
ladder.go
File metadata and controls
65 lines (54 loc) · 1.36 KB
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
package measurement
type ladderItem[U comparable] struct {
unit U
fromPrev int
}
// ladder is a sequence of units with conversion factors between adjacent units.
// Unit systems typically define units in a sequence with whole multipliers between units.
type ladder[U comparable] []ladderItem[U]
func (l ladder[U]) indexOf(unit U) int {
for i, item := range l {
if item.unit == unit {
return i
}
}
return -1
}
func convertByLadder[U comparable, T int32 | int64 | float32 | float64](amount T, from, to U, ladder ladder[U]) (T, bool) {
if from == to || amount == 0 {
return amount, true
}
idxFrom, idxTo := ladder.indexOf(from), ladder.indexOf(to)
if idxFrom == -1 || idxTo == -1 || idxFrom == idxTo {
return amount, false
}
var f int = 1
for idx := idxFrom; (idx + 1) <= idxTo; idx++ {
prev := f
f *= ladder[idx+1].fromPrev
if f/ladder[idx+1].fromPrev != prev {
return 0, false // factor overflow
}
}
for idx := idxFrom; idx > idxTo; idx-- {
prev := f
f *= ladder[idx].fromPrev
if f/ladder[idx].fromPrev != prev {
return 0, false // factor overflow
}
}
ft := T(f)
if idxFrom < idxTo {
if (amount/ft)*ft != amount {
return 0, false // loss of precision without fractions
}
amount /= ft
} else {
result := amount * ft
if result/ft != amount {
return 0, false // overflow
}
amount = result
}
return amount, true
}