Skip to content

Commit b9f8ec1

Browse files
math: Added RoundDecimalPlaces() and refactoring of IntPow, Sqrt and IsPrime (#187)
Made a few changes to IntPow, Sqrt and IsPrime Co-authored-by: Kashif Khan <[email protected]>
1 parent f5effb5 commit b9f8ec1

File tree

4 files changed

+110
-59
lines changed

4 files changed

+110
-59
lines changed

math/EXAMPLES.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,3 +460,27 @@ func main() {
460460
[1 24 2 12 3 8 4 6]
461461
```
462462
---
463+
## `RoundDecimalPlaces`
464+
465+
```go
466+
package main
467+
468+
import (
469+
"fmt"
470+
471+
utils "github.com/kashifkhan0771/utils/math"
472+
)
473+
474+
func main() {
475+
fmt.Println(utils.RoundDecimalPlaces(3.14159, 2)) // Rounds to 2 decimal places
476+
fmt.Println(utils.RoundDecimalPlaces(3.14159, -1)) // Negative places are clamped to 0 (rounds to a whole number)
477+
}
478+
```
479+
480+
#### Output:
481+
482+
```
483+
3.14
484+
3.0
485+
```
486+
---

math/README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,15 @@
2424

2525
- **LCM**: Finds the least common multiple (LCM) of two integers.
2626

27-
- **Sqrt**: Finds square root of the given number. Works for both integers and floating-point numbers. If the input is negative, it returns the initial given number.
28-
+ **Sqrt**: Finds the square root of the given number. Works for both integers and floating-point numbers. If the input is negative, it returns an error along with the original negative number.
27+
- **Sqrt**: Finds the square root of the given number. Works for both integers and floating-point numbers. If the input is negative, it returns an error along with the original negative number.
2928

3029
- **IsPrime**: Checks if a number is prime or not. Only works for non-negative integers.
3130

3231
- **PrimeList**: Returns a slice of prime numbers up to n.
3332

3433
- **GetDivisors**: Returns the divisors of a positive integer as an unordered slice. Returns an empty slice for negative inputs.
3534

36-
35+
- **RoundDecimalPlaces**: Rounds a float64 to the specified number of decimal places using `math.Round` (half away from zero). Negative values for `places` are clamped to `0` (i.e., rounds to a whole number).
3736

3837
## Examples:
3938

math/math.go

Lines changed: 21 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package math
22

33
import (
44
"errors"
5-
"log"
5+
"math"
66
)
77

88
// number is a type constraint that matches all numeric types (integers and floats).
@@ -72,35 +72,7 @@ func Clamp[T number](min, max, value T) T {
7272
// IntPow calculates base raised to the power of exp.
7373
// Supports both positive and negative exponents. Returns float64 for fractional results.
7474
func IntPow(base, exp int) float64 {
75-
if base == 0 && exp < 0 {
76-
log.Fatal("IntPow: Error, base 0 raised to a negative number simplifies to 1/0(Impossible).")
77-
}
78-
if exp == 0 {
79-
return 1 // Any number to the power of 0 is 1
80-
}
81-
82-
result := 1
83-
isNegative := exp < 0
84-
85-
// Use absolute value of exp for calculations
86-
if isNegative {
87-
exp = -exp
88-
}
89-
90-
for exp > 0 {
91-
if exp%2 == 1 {
92-
result *= base
93-
}
94-
base *= base
95-
exp /= 2
96-
}
97-
98-
// If the exponent was negative, return the reciprocal
99-
if isNegative {
100-
return 1 / float64(result)
101-
}
102-
103-
return float64(result)
75+
return math.Pow(float64(base), float64(exp))
10476
}
10577

10678
// IsEven checks if an integer x is even.
@@ -160,46 +132,31 @@ func LCM(x, y int) int {
160132
return (x / GCD(x, y)) * y
161133
}
162134

163-
// Sqrt computes the square root of a number using Newton's method.
135+
// Sqrt computes the square root of a number using the standard library's math.Sqrt.
136+
// For negative inputs, it returns the original value and an error.
164137
func Sqrt[T number](x T) (float64, error) {
165138
if x < 0 {
166139
return float64(x), errors.New("square root of a negative number is undefined")
167-
} else if x == 0 {
168-
return 0.0, nil
169140
}
170141

171-
epsilon := 1e-10 // Precision threshold
172-
z := float64(x) // Initial guess
173-
174-
for {
175-
nextZ := z - (z*z-float64(x))/(2*z)
176-
if Abs[float64](nextZ-z) < epsilon {
177-
return z, nil
178-
}
179-
z = nextZ
180-
}
142+
return math.Sqrt(float64(x)), nil
181143
}
182144

183-
// check if a number is a prime number complexity = O(sqrt(n)
145+
// IsPrime checks if a number is prime. Complexity = O(sqrt(n)).
184146
func IsPrime(x int) bool {
185-
// 1 and 0 are not primes and handle all negative numbers as non prime
186-
if x < 2 {
147+
// 1 and 0 are not primes and handle all negative numbers as non-prime.
148+
if x < 2 || (x != 2 && IsEven(x)) {
187149
return false
188150
}
151+
189152
// 2 is the only even number that is a prime number
190153
if x == 2 {
191154
return true
192155
}
193-
// No need to handle sqrt error since we elimnated negative numbers
194-
fsqrx, _ := Sqrt(x)
195-
sqrx := int(fsqrx)
196-
// If it's an even number that is not 2 then it's not a prime number
197-
if x%2 == 0 {
198-
return false
199-
}
200-
// Since we eliminated all even numbers we can just iterate over only odd numbers
201-
// I'm iterating up to sqrx+1 just to account for any rounding errors
202-
for i := 3; i <= sqrx+1; i += 2 {
156+
157+
// Since even numbers are eliminated, iterate over odd divisors only.
158+
// Use i*i <= x to avoid float conversions and rounding concerns.
159+
for i := 3; i*i <= x; i += 2 {
203160
if x%i == 0 {
204161
return false
205162
}
@@ -255,3 +212,11 @@ func GetDivisors(n int) []int {
255212
// The list is not sorted.
256213
return list
257214
}
215+
216+
// RoundDecimalPlaces rounds a float64 to the specified number of decimal places.
217+
// Negative values for places are clamped to 0 (i.e., rounds to a whole number).
218+
func RoundDecimalPlaces(val float64, places int) float64 {
219+
p := math.Pow10(max(places, 0))
220+
221+
return math.Round(val*p) / p
222+
}

math/math_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,69 @@ func TestGetDivisors(t *testing.T) {
884884
}
885885
}
886886

887+
func TestRoundDecimalPlaces(t *testing.T) {
888+
tests := []struct {
889+
name string
890+
value float64
891+
places int
892+
expected float64
893+
}{
894+
{
895+
name: "success - round to 2 decimal places",
896+
value: 3.14159,
897+
places: 2,
898+
expected: 3.14,
899+
},
900+
{
901+
name: "success - round to 3 decimal places",
902+
value: 2.718281828459045,
903+
places: 3,
904+
expected: 2.718,
905+
},
906+
{
907+
name: "success - round to 0 decimal places",
908+
value: 1.9999,
909+
places: 0,
910+
expected: 2.0,
911+
},
912+
{
913+
name: "success - round negative number to 1 decimal place",
914+
value: -1.2345,
915+
places: 1,
916+
expected: -1.2,
917+
},
918+
{
919+
name: "success - round to 4 decimal places",
920+
value: 0.123456789,
921+
places: 4,
922+
expected: 0.1235,
923+
},
924+
{
925+
name: "success - round to 5 decimal places",
926+
value: 0.123456789,
927+
places: 5,
928+
expected: 0.12346,
929+
},
930+
{
931+
name: "success - round to 0 decimal places with negative number",
932+
value: 3.14159,
933+
places: -1,
934+
expected: 3.0,
935+
},
936+
}
937+
938+
for _, tt := range tests {
939+
t.Run(tt.name, func(t *testing.T) {
940+
t.Parallel()
941+
942+
result := RoundDecimalPlaces(tt.value, tt.places)
943+
if result != tt.expected {
944+
t.Errorf("RoundDecimalPlaces(%v, %d) = %v; want %v", tt.value, tt.places, result, tt.expected)
945+
}
946+
})
947+
}
948+
}
949+
887950
// ================================================================================
888951
// ### BENCHMARKS
889952
// ================================================================================

0 commit comments

Comments
 (0)