看来我正在使用“我最喜欢的数据类型” SqlDecimal进入more woes。
我想知道这是否应该视为错误。
当我在SQL中将两个小数相乘时,我得到了预期的结果。当我通过SQLCLR函数运行相同的数字时,结果令人惊讶。
C#代码:
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
namespace TestMultiplySQLDecimal
{
public static class Multiplier
{
[SqlFunction(DataAccess=DataAccessKind.None, IsDeterministic = true,IsPrecise = true)]
public static SqlDecimal Multiply(SqlDecimal a, SqlDecimal b)
{
if (a.IsNull || b.IsNull) return SqlDecimal.Null;
return a*b;
}
}
}
SQL代码:
USE tempdb
GO
IF DB_ID('test') IS NOT NULL DROP DATABASE test
GO
CREATE DATABASE test
GO
USE test
GO
CREATE ASSEMBLY TestMultiplySQLDecimal
FROM 'C:\Users\tralalalaa\Documents\visual studio 2015\Projects\TestMultiplySQLDecimal\TestMultiplySQLDecimal\bin\Release\TestMultiplySQLDecimal.dll'
WITH PERMISSION_SET = SAFE
GO
CREATE FUNCTION dbo.fn_multiply(@a decimal(38,8), @b decimal(18,8))
RETURNS decimal(38,8)
EXTERNAL NAME TestMultiplySQLDecimal.[TestMultiplySQLDecimal.Multiplier].Multiply
GO
DECLARE @a decimal(38, 8),
@b decimal(18, 8),
@c decimal(38, 8),
@f decimal(38, 8)
SELECT @a = -0.00000450,
@b = 0.193,
@c = NULL,
@f = NULL
SELECT @c = @a * @b,
@f = dbo.fn_multiply(@a, @b)
SELECT multiply = null, c = @c, f = @f
结果是:
c = -0.00000100
f = +0.00000100
我知道“绝对”差异是“最小”,并且我已经“淡化”了更大的错误,将其归咎于“四舍五入的差异”……但是很难向客户解释,负数乘以正数会带来正数。
毕竟,T-SQL很好地支持了它……
我可以尝试通过使用小数(28,8)而不是小数(38,8)来解决此问题,但是我会遇到其他(完全不相关)的问题,然后=/
以下控制台应用程序也出现了相同的问题,而不必涉及SQL Server/SQLCLR:
using System;
using System.Data.SqlTypes;
namespace PlayAreaCSCon
{
class Program
{
static void Main(string[] args)
{
var dec1 = new SqlDecimal(-0.00000450d);
var dec2 = new SqlDecimal(0.193d);
dec1 = SqlDecimal.ConvertToPrecScale(dec1, 38, 8);
dec2 = SqlDecimal.ConvertToPrecScale(dec2, 18, 8);
Console.WriteLine(dec1 * dec2);
Console.ReadLine();
}
}
}
打印
0.000001
最佳答案
我相信该错误位于1550 of SqlDecimal
行周围:
ret = new SqlDecimal(rgulRes, (byte)culRes, (byte)ResPrec,
(byte)ActualScale, fResPositive);
if (ret.FZero ())
ret.SetPositive();
ret.AssertValid();
ret.AdjustScale(lScaleAdjust, true);
return ret;
它首先使用final scale参数构造一个新的十进制。接下来,它根据传入的构造函数参数检查结果是否为“零”。
然后,在断言所有内容都有效之后,它将执行比例调整。
在执行FZero检查时,结果类似于
-0.0000008685
。并且我们知道最终的小数位数将是6,因为我们处于结果scale and precision的极限。好吧,前6位全为零。只有在此之后,在调整比例时,才会考虑四舍五入并将
1
移至最后的小数位。这是一个错误。不幸的是,SQL Server native 实现
decimal
的源代码未公开可用,因此我们无法将其与SqlDecimal
的托管实现进行比较,以了解它们之间的相似程度以及原始版本如何避免相同的错误。关于c# - 小数乘法中的C#SqlDecimal翻转符号,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41938320/