Skip to content

Commit 0e6fb1f

Browse files
committed
Add safe absolute value operation
1 parent 21e93b5 commit 0e6fb1f

File tree

7 files changed

+301
-147
lines changed

7 files changed

+301
-147
lines changed

README.md

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,14 @@ go generate
2626

2727
#### Arithmetic overflow detection
2828
```go
29-
package main
30-
31-
import "fmt"
32-
import "math"
3329
import "github.com/rwxe/overflow"
3430

3531
func main() {
36-
addend := math.MaxInt64 - 5
37-
for i := 0; i < 10; i++ {
38-
sum, ok := overflow.Add(addend, i)
39-
fmt.Printf("%v+%v -> (%v,%v)\n",
40-
addend, i, sum, ok)
41-
}
32+
addend := math.MaxInt64 - 5
33+
for i := 0; i < 10; i++ {
34+
sum, ok := overflow.Add(addend, i)
35+
fmt.Printf("%v+%v -> (%v,%v)\n", addend, i, sum, ok)
36+
}
4237
}
4338
```
4439
yields the output
@@ -56,16 +51,14 @@ yields the output
5651
```
5752
For (u)int types, provide (U)Add, (U)Sub, (U)Mul, (U)Div, (U)Quotient, etc.
5853

59-
6054
#### Type conversion overflow detection
6155
```go
6256
func main() {
63-
var i uint
64-
for i = math.MaxInt - 5; i <= math.MaxInt+5; i++ {
65-
ret, ok := overflow.UintToInt(i)
66-
fmt.Printf("%v -> (%v,%v)\n",
67-
i, ret, ok)
68-
}
57+
var i uint
58+
for i = math.MaxInt - 5; i <= math.MaxInt+5; i++ {
59+
ret, ok := overflow.UintToInt(i)
60+
fmt.Printf("%v -> (%v,%v)\n", i, ret, ok)
61+
}
6962
}
7063
```
7164
yields the output
@@ -84,9 +77,41 @@ yields the output
8477
```
8578
Provide UintToInt, IntToUint, Uint64ToInt32, Int32ToUint64, etc.
8679

80+
#### Get absolute value
81+
```go
82+
func main() {
83+
normalAbs := func(x int64) int64 {
84+
if x < 0 {
85+
x = -x
86+
}
87+
return x
88+
}
89+
var i1, j1, k1 int64 = -9007199254740993, -9007199254740993, -9007199254740993
90+
fmt.Println(int64(math.Abs(float64(i1))))
91+
fmt.Println(normalAbs(j1))
92+
fmt.Println(overflow.Abs64(k1))
93+
94+
var i2, j2, k2 int64 = math.MinInt64, math.MinInt64, math.MinInt64
95+
fmt.Println(int64(math.Abs(float64(i2))))
96+
fmt.Println(normalAbs(j2))
97+
fmt.Println(overflow.Abs64(k2))
98+
}
99+
```
100+
yields the output
101+
```go
102+
9007199254740992 // Mantissa overflow, precision lost
103+
9007199254740993
104+
9007199254740993 true
105+
-9223372036854775808
106+
-9223372036854775808
107+
-9223372036854775808 false // Overflow detected
108+
```
109+
110+
For int, provides an absolute value including overflow detection.
111+
87112
### Stay calm and panic
88113

89-
There's a good case to be made that a panic is an unidiomatic but proper response. Iff you believe that there's no valid way to continue your program after math goes wayward, you can use the easier Addp, Mulp, Subp, Divp, IntToUintp, UintToIntp versions which return the normal result or panic.
114+
There's a good case to be made that a panic is an unidiomatic but proper response. If you believe that there's no valid way to continue your program after math goes wayward, you can use the easier Addp, Mulp, Subp, Divp, IntToUintp, Absp etc, which return the normal result or panic.
90115

91116
### Performance considerations
92117

README.zh_CN.md

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,41 @@ func main() {
8282
```
8383
提供UintToInt、IntToUint、Uint64ToInt32、Int32ToUint64等操作。
8484

85+
#### 取绝对值
86+
```go
87+
func main() {
88+
normalAbs := func(x int64) int64 {
89+
if x < 0 {
90+
x = -x
91+
}
92+
return x
93+
}
94+
var i1, j1, k1 int64 = -9007199254740993, -9007199254740993, -9007199254740993
95+
fmt.Println(int64(math.Abs(float64(i1))))
96+
fmt.Println(normalAbs(j1))
97+
fmt.Println(overflow.Abs64(k1))
98+
99+
var i2, j2, k2 int64 = math.MinInt64, math.MinInt64, math.MinInt64
100+
fmt.Println(int64(math.Abs(float64(i2))))
101+
fmt.Println(normalAbs(j2))
102+
fmt.Println(overflow.Abs64(k2))
103+
}
104+
```
105+
yields the output
106+
```go
107+
9007199254740992 // Mantissa overflow, precision lost
108+
9007199254740993
109+
9007199254740993 true
110+
-9223372036854775808
111+
-9223372036854775808
112+
-9223372036854775808 false // Overflow detected
113+
```
114+
115+
对于整数,提供包含溢出检测的取绝对值操作。
116+
85117
### 保持冷静并恐慌
86118

87-
有充分的证据表明,恐慌是一种不惯用但正确的反应。 如果你相信在算术和转换出现问题后,没有有效的方法可以继续你的程序,你可以使用更简单的 Addp、Mulp、Subp 、Divp、UintToIntp、IntToUintp 版本,它们返回正常结果或恐慌。
119+
有充分的证据表明,恐慌是一种不惯用但正确的反应。 如果你相信在算术和转换出现问题后,没有有效的方法可以继续你的程序,你可以使用更简单的 Addp、Mulp、Subp 、Divp、UintToIntp、Absp等版本,它们返回正常结果或恐慌。
88120

89121
### 性能考虑
90122

overflow.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@ So, FEEL FREE to carefully review the code visually.
4242

4343
// Unspecified size, i.e. normal signed int
4444

45+
// Abs get absolute value of an int, returning the result and a ok result indicating whether the operation is safe.
46+
func Abs(x int) (int, bool) {
47+
if _is64Bit() {
48+
r64, ok := Abs64(int64(x))
49+
return int(r64), ok
50+
}
51+
r32, ok := Abs32(int32(x))
52+
return int(r32), ok
53+
}
54+
4555
// Add sums two ints, returning the result and a ok result indicating whether the operation is safe.
4656
func Add(a, b int) (int, bool) {
4757
if _is64Bit() {
@@ -166,6 +176,15 @@ func UintToInt(x uint) (int, bool) {
166176

167177
/************* Panic versions for int ****************/
168178

179+
// Absp returns the absolute value, panicking on overflow
180+
func Absp(x int) int {
181+
r, ok := Abs(x)
182+
if !ok {
183+
panic("absolute value overflow")
184+
}
185+
return r
186+
}
187+
169188
// Addp returns the sum of two ints, panicking on overflow
170189
func Addp(a, b int) int {
171190
r, ok := Add(a, b)

overflow_impl.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,18 @@ import "math"
88

99

1010

11+
// Abs8 performs absolute value operation on an int8 operand
12+
// returning a result and a ok result indicating whether the operation is safe.
13+
func Abs8(x int8) (int8, bool) {
14+
if x == math.MinInt8 {
15+
return x, false
16+
}
17+
if x >= 0 {
18+
return x, true
19+
}
20+
return -x, true
21+
}
22+
1123
// Add8 performs + operation on two int8 operands
1224
// returning a result and a ok result indicating whether the operation is safe.
1325
func Add8(a, b int8) (int8, bool) {
@@ -188,6 +200,18 @@ func UQuotient8(a, b uint8) (uint8, uint8, bool) {
188200

189201

190202

203+
// Abs16 performs absolute value operation on an int16 operand
204+
// returning a result and a ok result indicating whether the operation is safe.
205+
func Abs16(x int16) (int16, bool) {
206+
if x == math.MinInt16 {
207+
return x, false
208+
}
209+
if x >= 0 {
210+
return x, true
211+
}
212+
return -x, true
213+
}
214+
191215
// Add16 performs + operation on two int16 operands
192216
// returning a result and a ok result indicating whether the operation is safe.
193217
func Add16(a, b int16) (int16, bool) {
@@ -368,6 +392,18 @@ func UQuotient16(a, b uint16) (uint16, uint16, bool) {
368392

369393

370394

395+
// Abs32 performs absolute value operation on an int32 operand
396+
// returning a result and a ok result indicating whether the operation is safe.
397+
func Abs32(x int32) (int32, bool) {
398+
if x == math.MinInt32 {
399+
return x, false
400+
}
401+
if x >= 0 {
402+
return x, true
403+
}
404+
return -x, true
405+
}
406+
371407
// Add32 performs + operation on two int32 operands
372408
// returning a result and a ok result indicating whether the operation is safe.
373409
func Add32(a, b int32) (int32, bool) {
@@ -548,6 +584,18 @@ func UQuotient32(a, b uint32) (uint32, uint32, bool) {
548584

549585

550586

587+
// Abs64 performs absolute value operation on an int64 operand
588+
// returning a result and a ok result indicating whether the operation is safe.
589+
func Abs64(x int64) (int64, bool) {
590+
if x == math.MinInt64 {
591+
return x, false
592+
}
593+
if x >= 0 {
594+
return x, true
595+
}
596+
return -x, true
597+
}
598+
551599
// Add64 performs + operation on two int64 operands
552600
// returning a result and a ok result indicating whether the operation is safe.
553601
func Add64(a, b int64) (int64, bool) {

overflow_template.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,18 @@ for SIZE in 8 16 32 64
1616
do
1717
echo "
1818
19+
// Abs${SIZE} performs absolute value operation on an int${SIZE} operand
20+
// returning a result and a ok result indicating whether the operation is safe.
21+
func Abs${SIZE}(x int${SIZE}) (int${SIZE}, bool) {
22+
if x == math.MinInt${SIZE} {
23+
return x, false
24+
}
25+
if x >= 0 {
26+
return x, true
27+
}
28+
return -x, true
29+
}
30+
1931
// Add${SIZE} performs + operation on two int${SIZE} operands
2032
// returning a result and a ok result indicating whether the operation is safe.
2133
func Add${SIZE}(a, b int${SIZE}) (int${SIZE}, bool) {

overflow_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,24 @@ func TestQuotient(t *testing.T) {
201201
}
202202
}
203203

204+
func TestAbs(t *testing.T) {
205+
if _, ok := Abs(math.MinInt); ok {
206+
t.Error("unexpected ok, absolute value overflow")
207+
}
208+
if _, ok := Abs64(math.MinInt64); ok {
209+
t.Error("unexpected ok, absolute value overflow")
210+
}
211+
if _, ok := Abs32(math.MinInt32); ok {
212+
t.Error("unexpected ok, absolute value overflow")
213+
}
214+
if _, ok := Abs16(math.MinInt16); ok {
215+
t.Error("unexpected ok, absolute value overflow")
216+
}
217+
if _, ok := Abs8(math.MinInt8); ok {
218+
t.Error("unexpected ok, absolute value overflow")
219+
}
220+
}
221+
204222
//func TestAdditionInt(t *testing.T) {
205223
// fmt.Printf("\nminint8 = %v\n", math.MinInt8)
206224
// fmt.Printf("maxint8 = %v\n\n", math.MaxInt8)

0 commit comments

Comments
 (0)