From 1045fead8d2ed9737c81a29e1bfcb2282204993d Mon Sep 17 00:00:00 2001 From: weirdgiraffe Date: Wed, 8 Jan 2025 13:22:08 +0100 Subject: [PATCH 1/2] feat: add sqrt funcions --- decimal.go | 13 +++++++++++++ decimal_test.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/decimal.go b/decimal.go index a37a230..23fb867 100644 --- a/decimal.go +++ b/decimal.go @@ -945,6 +945,19 @@ func (d Decimal) powBigIntWithPrecision(exp *big.Int, precision int32) (Decimal, return result, nil } +// Sqrt returns the square root of d, accurate to [DivisionPrecision] decimal places. +func (d Decimal) Sqrt() Decimal { + return d.SqrtRound(int32(DivisionPrecision)) +} + +// SqtRound returns the square root of the decimal with a given precision. +func (d Decimal) SqrtRound(precision int32) Decimal { + // rescale to make an integer with enough digits to reach the desired precision + bi := d.rescale(-2 * precision).Coefficient() + sqrt := new(big.Int).Sqrt(bi) + return NewFromBigInt(sqrt, -1*precision) +} + // ExpHullAbrham calculates the natural exponent of decimal (e to the power of d) using Hull-Abraham algorithm. // OverallPrecision argument specifies the overall precision of the result (integer part + decimal part). // diff --git a/decimal_test.go b/decimal_test.go index d398f2d..bf0d903 100644 --- a/decimal_test.go +++ b/decimal_test.go @@ -2833,6 +2833,54 @@ func TestDecimal_PowBigInt_UndefinedResult(t *testing.T) { } } +func TestDecimal_SqrtRound(t *testing.T) { + t.Run("panic on negative number", func(t *testing.T) { + if !didPanic(func() { + _ = NewFromInt(-1).Sqrt() + }) { + t.Fatal("should panic on negative numbers") + } + }) + + var tt = []struct { + In string + Out string + Precision int32 + }{ + {In: "0.0", Out: "0.0"}, + {In: "1.0", Out: "1.0"}, + {In: "4.0", Out: "2.0"}, + {In: "3.0", Out: "1.7320508075688772"}, + {In: "0.002342", Out: "0.0483942145302514"}, + {In: "0.002342", Out: "0.0483", Precision: 4}, + {In: "0.002342", Out: "0.04839421453025144409653399922499", Precision: 32}, + {In: "4.5", Out: "2.1213203435596425"}, + {In: "3289854.0", Out: "1813.7954680724064484"}, + } + for _, tc := range tt { + t.Run(tc.In, func(t *testing.T) { + v, err := NewFromString(tc.In) + if err != nil { + t.Fatalf("error parsing test value into Decimal") + } + + e, err := NewFromString(tc.Out) + if err != nil { + t.Fatalf("error parsing test expected value into Decimal") + } + + if tc.Precision == 0 { + tc.Precision = int32(DivisionPrecision) + } + + sqrt := v.SqrtRound(tc.Precision) + if !sqrt.Equal(e) { + t.Fatalf("Square root of %s should be %s, not %s", v, e, sqrt) + } + }) + } +} + func TestDecimal_IsInteger(t *testing.T) { for _, testCase := range []struct { Dec string From 95d201086ab867d37f667210340d8bd57aa68b9c Mon Sep 17 00:00:00 2001 From: weirdgiraffe Date: Wed, 8 Jan 2025 16:23:32 +0100 Subject: [PATCH 2/2] fixup: use pow function for getting the sqrt --- decimal.go | 9 +++++---- decimal_test.go | 6 +++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/decimal.go b/decimal.go index 23fb867..2ae5c00 100644 --- a/decimal.go +++ b/decimal.go @@ -952,10 +952,11 @@ func (d Decimal) Sqrt() Decimal { // SqtRound returns the square root of the decimal with a given precision. func (d Decimal) SqrtRound(precision int32) Decimal { - // rescale to make an integer with enough digits to reach the desired precision - bi := d.rescale(-2 * precision).Coefficient() - sqrt := new(big.Int).Sqrt(bi) - return NewFromBigInt(sqrt, -1*precision) + res, err := d.PowWithPrecision(NewFromFloat(0.5), precision) + if err != nil { + panic(err) + } + return res.Round(precision) } // ExpHullAbrham calculates the natural exponent of decimal (e to the power of d) using Hull-Abraham algorithm. diff --git a/decimal_test.go b/decimal_test.go index bf0d903..8e7e78c 100644 --- a/decimal_test.go +++ b/decimal_test.go @@ -2850,11 +2850,11 @@ func TestDecimal_SqrtRound(t *testing.T) { {In: "0.0", Out: "0.0"}, {In: "1.0", Out: "1.0"}, {In: "4.0", Out: "2.0"}, - {In: "3.0", Out: "1.7320508075688772"}, + {In: "3.0", Out: "1.7320508075688773"}, {In: "0.002342", Out: "0.0483942145302514"}, - {In: "0.002342", Out: "0.0483", Precision: 4}, + {In: "0.002342", Out: "0.0484", Precision: 4}, {In: "0.002342", Out: "0.04839421453025144409653399922499", Precision: 32}, - {In: "4.5", Out: "2.1213203435596425"}, + {In: "4.5", Out: "2.1213203435596426"}, {In: "3289854.0", Out: "1813.7954680724064484"}, } for _, tc := range tt {