mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-22 04:36:42 +00:00
common/math: add safe division, modulo, and utility functions
Add new integer utility functions to common/math package: - SafeDiv: safe division that returns error flag on division by zero - SafeMod: safe modulo that returns error flag on modulo by zero - Min: returns minimum of two uint64 values - Max: returns maximum of two uint64 values - Clamp: constrains value to a range [min, max] - AbsDiff: returns absolute difference between two uint64 values These utilities provide commonly needed operations with proper edge case handling, particularly useful for gas calculations and other EVM-related integer operations where division/modulo by zero must be handled gracefully. Includes comprehensive tests for all new functions.
This commit is contained in:
parent
f51870e40e
commit
594a5b3098
2 changed files with 240 additions and 0 deletions
76
common/math/integer_additions.go
Normal file
76
common/math/integer_additions.go
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
// Copyright 2015 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package math
|
||||||
|
|
||||||
|
// SafeDiv returns x/y and checks for division by zero.
|
||||||
|
// Returns (0, true) if y is zero (indicating an error condition).
|
||||||
|
// Returns (x/y, false) if the division is valid.
|
||||||
|
func SafeDiv(x, y uint64) (uint64, bool) {
|
||||||
|
if y == 0 {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
return x / y, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// SafeMod returns x%y and checks for division by zero.
|
||||||
|
// Returns (0, true) if y is zero (indicating an error condition).
|
||||||
|
// Returns (x%y, false) if the operation is valid.
|
||||||
|
func SafeMod(x, y uint64) (uint64, bool) {
|
||||||
|
if y == 0 {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
return x % y, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Min returns the smaller of x or y.
|
||||||
|
func Min(x, y uint64) uint64 {
|
||||||
|
if x < y {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
|
||||||
|
// Max returns the larger of x or y.
|
||||||
|
func Max(x, y uint64) uint64 {
|
||||||
|
if x > y {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clamp returns x clamped to the inclusive range [min, max].
|
||||||
|
// If x is less than min, min is returned.
|
||||||
|
// If x is greater than max, max is returned.
|
||||||
|
// Otherwise, x is returned unchanged.
|
||||||
|
func Clamp(x, min, max uint64) uint64 {
|
||||||
|
if x < min {
|
||||||
|
return min
|
||||||
|
}
|
||||||
|
if x > max {
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
// AbsDiff returns the absolute difference between x and y.
|
||||||
|
// This is useful when you need |x - y| without worrying about underflow.
|
||||||
|
func AbsDiff(x, y uint64) uint64 {
|
||||||
|
if x > y {
|
||||||
|
return x - y
|
||||||
|
}
|
||||||
|
return y - x
|
||||||
|
}
|
||||||
164
common/math/integer_additions_test.go
Normal file
164
common/math/integer_additions_test.go
Normal file
|
|
@ -0,0 +1,164 @@
|
||||||
|
// Copyright 2015 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package math
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSafeDiv(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
x, y uint64
|
||||||
|
expected uint64
|
||||||
|
isError bool
|
||||||
|
}{
|
||||||
|
{10, 2, 5, false},
|
||||||
|
{10, 3, 3, false},
|
||||||
|
{0, 5, 0, false},
|
||||||
|
{100, 1, 100, false},
|
||||||
|
{math.MaxUint64, 2, math.MaxUint64 / 2, false},
|
||||||
|
// Division by zero cases
|
||||||
|
{10, 0, 0, true},
|
||||||
|
{0, 0, 0, true},
|
||||||
|
{math.MaxUint64, 0, 0, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
result, isError := SafeDiv(test.x, test.y)
|
||||||
|
if isError != test.isError {
|
||||||
|
t.Errorf("test %d: SafeDiv(%d, %d) error = %v, want %v", i, test.x, test.y, isError, test.isError)
|
||||||
|
}
|
||||||
|
if !isError && result != test.expected {
|
||||||
|
t.Errorf("test %d: SafeDiv(%d, %d) = %d, want %d", i, test.x, test.y, result, test.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSafeMod(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
x, y uint64
|
||||||
|
expected uint64
|
||||||
|
isError bool
|
||||||
|
}{
|
||||||
|
{10, 3, 1, false},
|
||||||
|
{10, 2, 0, false},
|
||||||
|
{0, 5, 0, false},
|
||||||
|
{7, 7, 0, false},
|
||||||
|
{math.MaxUint64, 2, 1, false},
|
||||||
|
// Modulo by zero cases
|
||||||
|
{10, 0, 0, true},
|
||||||
|
{0, 0, 0, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
result, isError := SafeMod(test.x, test.y)
|
||||||
|
if isError != test.isError {
|
||||||
|
t.Errorf("test %d: SafeMod(%d, %d) error = %v, want %v", i, test.x, test.y, isError, test.isError)
|
||||||
|
}
|
||||||
|
if !isError && result != test.expected {
|
||||||
|
t.Errorf("test %d: SafeMod(%d, %d) = %d, want %d", i, test.x, test.y, result, test.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMin(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
x, y uint64
|
||||||
|
expected uint64
|
||||||
|
}{
|
||||||
|
{1, 2, 1},
|
||||||
|
{2, 1, 1},
|
||||||
|
{5, 5, 5},
|
||||||
|
{0, 100, 0},
|
||||||
|
{math.MaxUint64, 0, 0},
|
||||||
|
{math.MaxUint64, math.MaxUint64 - 1, math.MaxUint64 - 1},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
result := Min(test.x, test.y)
|
||||||
|
if result != test.expected {
|
||||||
|
t.Errorf("test %d: Min(%d, %d) = %d, want %d", i, test.x, test.y, result, test.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMax(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
x, y uint64
|
||||||
|
expected uint64
|
||||||
|
}{
|
||||||
|
{1, 2, 2},
|
||||||
|
{2, 1, 2},
|
||||||
|
{5, 5, 5},
|
||||||
|
{0, 100, 100},
|
||||||
|
{math.MaxUint64, 0, math.MaxUint64},
|
||||||
|
{math.MaxUint64, math.MaxUint64 - 1, math.MaxUint64},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
result := Max(test.x, test.y)
|
||||||
|
if result != test.expected {
|
||||||
|
t.Errorf("test %d: Max(%d, %d) = %d, want %d", i, test.x, test.y, result, test.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClamp(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
x, min, max uint64
|
||||||
|
expected uint64
|
||||||
|
}{
|
||||||
|
{5, 0, 10, 5}, // within range
|
||||||
|
{0, 5, 10, 5}, // below min
|
||||||
|
{15, 5, 10, 10}, // above max
|
||||||
|
{5, 5, 10, 5}, // at min
|
||||||
|
{10, 5, 10, 10}, // at max
|
||||||
|
{5, 5, 5, 5}, // min == max == x
|
||||||
|
{0, 0, 0, 0}, // all zero
|
||||||
|
{100, 0, 50, 50}, // clamped to max
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
result := Clamp(test.x, test.min, test.max)
|
||||||
|
if result != test.expected {
|
||||||
|
t.Errorf("test %d: Clamp(%d, %d, %d) = %d, want %d", i, test.x, test.min, test.max, result, test.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAbsDiff(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
x, y uint64
|
||||||
|
expected uint64
|
||||||
|
}{
|
||||||
|
{10, 3, 7},
|
||||||
|
{3, 10, 7},
|
||||||
|
{5, 5, 0},
|
||||||
|
{0, 0, 0},
|
||||||
|
{math.MaxUint64, 0, math.MaxUint64},
|
||||||
|
{0, math.MaxUint64, math.MaxUint64},
|
||||||
|
{100, 99, 1},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
result := AbsDiff(test.x, test.y)
|
||||||
|
if result != test.expected {
|
||||||
|
t.Errorf("test %d: AbsDiff(%d, %d) = %d, want %d", i, test.x, test.y, result, test.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue