mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-21 20:26:41 +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