-
Notifications
You must be signed in to change notification settings - Fork 630
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Optimize NumDigits #356
Optimize NumDigits #356
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks again @serprex for your contribution! :3 Optimizing NumDigits
was the next task on the roadmap, so I'm really happy that you worked on that :D
Unfortunately, I've spotted a small bug. The new implementation doesn't work for all potential decimals. For example decimal with 500 digits in base and 500 digits in fractional part, instead of returning 1000 it returns 999. By looking at your implementation I think an error correction check should be only applied when check >= 0
, thus I think we should do something like this:
abs := new(big.Int).Abs(d.value)
estimatedNumDigits := int(float64(abs.BitLen()) / math.Log2(10))
// estimatedNumDigits (lg10) may be off by 1, need to verify
digitsBigInt := big.NewInt(int64(estimatedNumDigits))
errorCorrectionUnit := digitsBigInt.Exp(tenInt, digitsBigInt, nil)
if abs.Cmp(errorCorrectionUnit) >= 0 {
return estimatedNumDigits + 1
}
return estimatedNumDigits
What do you think?
Updated with your suggestion. Some fuzzing against previous algo would be ideal, but seems good. Adding |
Dividing BitLen by math.Log2(10) is what math/big does underneath Not including the Int64/Uint64 check makes this slightly slower than old method Included 2 benchmarks, for 10 digit numbers & 100 digit numbers: -- before > go test -bench=NumDigit -run=NumDigit goos: linux goarch: amd64 pkg: github.com/shopspring/decimal cpu: AMD Ryzen 7 7840U w/ Radeon 780M Graphics BenchmarkDecimal_NumDigits10-16 18317293 63.87 ns/op BenchmarkDecimal_NumDigits100-16 3645015 329.6 ns/op -- after ... BenchmarkDecimal_NumDigits10-16 143781325 8.488 ns/op BenchmarkDecimal_NumDigits100-16 5931247 207.4 ns/op
9007199254740992 converts to float64
Locally, I've run this function against 100k random decimals and compared it with the old implementation. It seems working fine after adjustments :3 But yea, I agree that we need to introduce a more sophisticated test suite one day. Simple unit testing is not enough if we operate on decimals with high precision. |
After the CI fail, I realized that CI only runs against the |
Oh :< We overlooked one aspect @serprex. |
@mwoss not sure what constraints determine your minversion, but 1.10 was released 6 years ago & the go compiler works to have backwards compatibility based on go.mod value that I'd be surprised if it's a problem raising minversion |
I just wanted to support the oldest Go version possible, this was the main reason. But maybe you're right and I'm exaggerating a bit here. When I started working on this library Go1.7 was only 3 years old, now it's already 8. Rather few people use such an old version of Go, plus what you said about go compiler backwards compatibility. |
decimal 1.4.0 includes a couple new constructors for uint64 & *big.Rat It also includes a much faster NumDigits implementation which we use for snowflake sanitization: shopspring/decimal#356
decimal 1.4.0 includes a couple new constructors for uint64 & *big.Rat It also includes a much faster NumDigits implementation which we use for snowflake sanitization: shopspring/decimal#356
decimal 1.4.0 includes a couple new constructors for uint64 & *big.Rat It also includes a much faster NumDigits implementation which we use for snowflake sanitization: shopspring/decimal#356
Version here was an earlier incorrect version than what I eventually contributed to shopspring decimal See shopspring/decimal#356
Dividing BitLen by math.Log2(10) is what math/big does underneath
Not including the Int64/Uint64 check makes this slightly slower than old method for small integers
Included 2 benchmarks, for 10 digit numbers & 100 digit numbers: