Skip to content

Commit 3cb6b40

Browse files
authored
feat: Add public NewFromDecimal which outperforms decimal -> string -> alpacadecimal (#13)
* add decimal -> alpacadecimal conversion (21/01/2025 signed) * fix: adjust field alignment (#12) * refactor: use convention as @neal suggested (21/01/2025 signed)
1 parent f5de4ac commit 3cb6b40

File tree

3 files changed

+79
-3
lines changed

3 files changed

+79
-3
lines changed

benchmark_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,3 +360,38 @@ func BenchmarkRound(b *testing.B) {
360360
_ = result
361361
})
362362
}
363+
364+
func BenchmarkNewFromDecimal(b *testing.B) {
365+
b.Run("alpacadecimal.Decimal.NewFromDecimal", func(b *testing.B) {
366+
d := decimal.New(123, -12)
367+
368+
var result alpacadecimal.Decimal
369+
370+
b.ResetTimer()
371+
for n := 0; n < b.N; n++ {
372+
result = alpacadecimal.NewFromDecimal(d)
373+
}
374+
_ = result
375+
})
376+
377+
b.Run("alpacadecimal.Decimal.RequireFromString", func(b *testing.B) {
378+
d := decimal.New(123, -12)
379+
380+
var result alpacadecimal.Decimal
381+
382+
b.ResetTimer()
383+
for n := 0; n < b.N; n++ {
384+
result = alpacadecimal.RequireFromString(d.String())
385+
}
386+
_ = result
387+
})
388+
389+
b.Run("alpacadecimal.Decimal.New", func(b *testing.B) {
390+
var result alpacadecimal.Decimal
391+
for n := 0; n < b.N; n++ {
392+
result = alpacadecimal.New(123, -12)
393+
}
394+
_ = result
395+
})
396+
397+
}

decimal.go

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,20 +126,28 @@ func Min(first Decimal, rest ...Decimal) Decimal {
126126
// optimized:
127127
// New returns a new fixed-point decimal, value * 10 ^ exp.
128128
func New(value int64, exp int32) Decimal {
129+
d, ok := tryOptNew(value, exp)
130+
if ok {
131+
return d
132+
}
133+
return newFromDecimal(decimal.New(value, exp))
134+
}
135+
136+
func tryOptNew(value int64, exp int32) (Decimal, bool) {
129137
if exp >= -12 {
130138
if exp <= 0 {
131139
s := pow10Table[-exp]
132140
if value >= minInt*s && value <= maxInt*s {
133-
return Decimal{fixed: value * pow10Table[precision+exp]}
141+
return Decimal{fixed: value * pow10Table[precision+exp]}, true
134142
}
135143
} else if exp <= 6 { // when exp > 6, it would be greater than maxInt
136144
s := pow10Table[exp]
137145
if value >= minInt/s && value <= maxInt/s {
138-
return Decimal{fixed: value * pow10Table[precision+exp]}
146+
return Decimal{fixed: value * pow10Table[precision+exp]}, true
139147
}
140148
}
141149
}
142-
return newFromDecimal(decimal.New(value, exp))
150+
return Decimal{}, false
143151
}
144152

145153
// fallback:
@@ -1100,6 +1108,23 @@ func (d NullDecimal) Value() (driver.Value, error) {
11001108
return d.Decimal.Value()
11011109
}
11021110

1111+
// optimized:
1112+
// Create a new alpacadecimal.Decimal from a decimal.Decimal.
1113+
// Attempts to set the fixed value if possible.
1114+
func NewFromDecimal(d decimal.Decimal) Decimal {
1115+
co := d.Coefficient()
1116+
if !co.IsInt64() {
1117+
return newFromDecimal(d) // fallback
1118+
}
1119+
value := co.Int64()
1120+
exp := d.Exponent()
1121+
res, ok := tryOptNew(value, exp)
1122+
if ok {
1123+
return res
1124+
}
1125+
return newFromDecimal(d)
1126+
}
1127+
11031128
// internal implementation
11041129
func newFromDecimal(d decimal.Decimal) Decimal {
11051130
return Decimal{fallback: &d}

decimal_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,22 @@ func TestDecimal(t *testing.T) {
198198
require.Equal(t, x.String(), y.String())
199199
})
200200

201+
t.Run("NewFromDecimal", func(t *testing.T) {
202+
// first, with optimized decimal
203+
x := alpacadecimal.NewFromDecimal(decimal.New(123, -2))
204+
y := alpacadecimal.New(123, -2)
205+
shouldEqual(t, x, y)
206+
207+
// the prior means of conversion from decimal commonly used
208+
y = alpacadecimal.RequireFromString(decimal.New(123, -2).String())
209+
shouldEqual(t, x, y)
210+
211+
// now, with out of optimization range decimal
212+
x = alpacadecimal.NewFromDecimal(decimal.New(123, -13))
213+
y = alpacadecimal.New(123, -13)
214+
shouldEqual(t, x, y)
215+
})
216+
201217
t.Run("NewFromInt", func(t *testing.T) {
202218
x := alpacadecimal.NewFromInt(123)
203219
y, err := alpacadecimal.NewFromString("123")

0 commit comments

Comments
 (0)