Created
December 6, 2023 10:08
-
-
Save maxpushka/1a82e5bed4ab16d63379af7de349cc2d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package precision | |
import ( | |
"math" | |
"github.com/shopspring/decimal" | |
) | |
func ToSignificant(input decimal.Decimal, sigDigits int32, maxScale int32) decimal.Decimal { | |
if input.Equal(decimal.Zero) { | |
return input | |
} | |
integralDigits := int32(len(input.Abs().Coefficient().String())) | |
scale := input.Exponent() | |
if scale > 0 { | |
scale = 0 | |
} else { | |
scale = -scale | |
} | |
adjustedScale := sigDigits - (integralDigits - scale) | |
if adjustedScale < 0 { | |
// If the number of integral digits is greater than significant digits, | |
// round the number to a scale that maintains the significant digits in the integral part. | |
roundedValue := input.RoundBank(0) | |
multiplier := decimal.NewFromInt(int64(math.Pow10(int(adjustedScale * -1)))) | |
return roundedValue.DivRound(multiplier, 0).Mul(multiplier) | |
} else if adjustedScale > maxScale { | |
adjustedScale = maxScale | |
} | |
return input.RoundBank(adjustedScale) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package precision | |
import ( | |
"fmt" | |
"math" | |
"testing" | |
"github.com/shopspring/decimal" | |
) | |
func TestToSignificant(t *testing.T) { | |
tests := []struct { | |
input decimal.Decimal | |
expected decimal.Decimal | |
}{ | |
{decimal.NewFromFloat(0.0), decimal.NewFromFloat(0.0)}, | |
{decimal.NewFromFloat(math.SmallestNonzeroFloat64), decimal.NewFromFloat(0.0)}, | |
{decimal.NewFromFloat(0.0000000000000004), decimal.NewFromFloat(0.0000000000000004)}, | |
{decimal.NewFromFloat(0.00000000000000004), decimal.NewFromFloat(0)}, | |
{decimal.NewFromFloat(0.0001), decimal.NewFromFloat(0.0001)}, | |
{decimal.NewFromFloat(0.000103456), decimal.NewFromFloat(0.00010346)}, | |
{decimal.NewFromFloat(0.012344789), decimal.NewFromFloat(0.012345)}, | |
{decimal.NewFromFloat(0.001023499), decimal.NewFromFloat(0.0010235)}, | |
{decimal.NewFromFloat(0.012345), decimal.NewFromFloat(0.012345)}, | |
{decimal.NewFromFloat(1.00000234), decimal.NewFromFloat(1.0)}, | |
{decimal.NewFromInt(2), decimal.NewFromInt(2)}, | |
{decimal.NewFromInt(100006), decimal.NewFromInt(100010)}, | |
// Two more cases | |
{decimal.NewFromFloat(0.012345928), decimal.NewFromFloat(0.012346)}, | |
{decimal.NewFromFloat(0.001023499), decimal.NewFromFloat(0.0010235)}, | |
} | |
for _, tt := range tests { | |
tt := tt | |
t.Run(fmt.Sprintf("%s -> %s", tt.input, tt.expected), func(t *testing.T) { | |
actual := ToSignificant(tt.input, 5, 16) | |
if !actual.Equals(tt.expected) { | |
t.Errorf("ToSignificant(%s): expected %s, got %s", tt.input, tt.expected, actual) | |
} | |
}) | |
} | |
} | |
func BenchmarkToSignificant_DecimalWithLeadingZeros(b *testing.B) { | |
// Reset timer to exclude time taken by setup operations | |
// before the actual benchmark begins | |
b.ReportAllocs() | |
b.ResetTimer() | |
for i := 0; i < b.N; i++ { | |
ToSignificant(decimal.NewFromFloat(0.000123456), 5, 16) | |
} | |
} | |
func BenchmarkToSignificant_TruncateDecimals(b *testing.B) { | |
// Reset timer to exclude time taken by setup operations | |
// before the actual benchmark begins | |
b.ReportAllocs() | |
b.ResetTimer() | |
for i := 0; i < b.N; i++ { | |
ToSignificant(decimal.NewFromFloat(1.00000234), 5, 16) | |
} | |
} | |
func BenchmarkToSignificant_IntegralPartSizeGreaterThanSignificantDigits(b *testing.B) { | |
// Reset timer to exclude time taken by setup operations | |
// before the actual benchmark begins | |
b.ReportAllocs() | |
b.ResetTimer() | |
for i := 0; i < b.N; i++ { | |
ToSignificant(decimal.NewFromInt(100006), 5, 16) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment