看来我正在使用“我最喜欢的数据类型” 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/

10-10 03:35