-
Notifications
You must be signed in to change notification settings - Fork 3
Get rid of unsafe
code from TruncateToNetDecimal()
implemetation
#397
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
Conversation
Why not to use https://learn.microsoft.com/en-us/dotnet/api/system.data.sqltypes.sqldecimal.round for example? |
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.
Pull Request Overview
The PR removes unsafe code in TruncateToNetDecimal()
by leveraging the new UInt128
type for big-integer arithmetic and replaces manual division logic with UInt128.DivRem()
. Key changes include updating the PowersOf10
array to UInt128[]
, introducing Max96bitValue
and Ten
constants, and refactoring the scaling/truncation logic to use UInt128
.
- Replace unsafe pointer-based operations with safe
UInt128
arithmetic - Remove custom
DivHasRem()
and useUInt128.DivRem()
for division and remainder - Add constants (
PowersOf10
,Max96bitValue
,Ten
) to support new arithmetic
Comments suppressed due to low confidence (4)
Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/InternalHelpers.cs:15
- Consider adding a brief comment above the
PowersOf10
declaration explaining that these are precomputed 10^n values used for scaling optimizations.
private static readonly UInt128[] PowersOf10 = [
Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/InternalHelpers.cs:28
- [nitpick] It may help maintainers to include a comment describing that
Max96bitValue
represents the maximum value storable in 96 bits (2^96-1).
private static readonly UInt128 Max96bitValue = new(0xFFFFFFFFUL, ulong.MaxValue);
Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/InternalHelpers.cs:167
- [nitpick] The name
dividerPow
is a bit terse; consider renaming topowerOfTenIndex
ormaxDivisionPower
for clarity.
var dividerPow = maxZeroCount > 9 ? (byte) 9 : maxZeroCount;
Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/InternalHelpers.cs:182
- Add unit tests covering scenarios where the original value exceeds 96 bits and triggers the overflow branch, to ensure behavior matches the previous implementation.
for (; realScale > 0 && data > Max96bitValue; realScale--) {
Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/InternalHelpers.cs
Outdated
Show resolved
Hide resolved
I want to keep the original DO's logic |
…rs.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/InternalHelpers.cs
Outdated
Show resolved
Hide resolved
…rs.cs Co-authored-by: Sergey Naumenko <152863015+snaumenko-st@users.noreply.github.com>
…rs.cs Co-authored-by: Sergey Naumenko <152863015+snaumenko-st@users.noreply.github.com>
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.
This code is still hard to follow. But it's already better than before.
Maybe we can even get rid of PowersOf10
array for simplicity.
I agree and have removed it. |
try { | ||
return sqlDecimal.Value; // throws an OverflowException if the value is out of the decimal range. | ||
} | ||
catch (OverflowException) { |
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.
What is the probability of this in real use cases?
Exceptions are not cheap if it's not a rare case.
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.
I never seen OverflowException
here during local App runs.
I suppose that money values in the real DB don't have > 28 decimal places.
Some aggregations like AVG()
can, but their invocation is a rare case comparing
field access when we avoid array allocation by SqlDecimal.Data
It looks as processing of an extreme case.
We can use
UInt128
type for big-integer arithmetic instead of pinning arrays.DivHasRem()
can be replaced byUInt128.DivRem()
Try
sqlDecimal.Value
inside try/catch first to avoid array allocaton bySqDecimal.Data