【问题标题】:Testing differences between SqlDecimal and C# Decimal测试 SqlDecimal 和 C# Decimal 之间的差异
【发布时间】:2024-01-09 02:47:01
【问题描述】:

我正在尝试验证 c# 中的小数是否适合 db 列小数。 SqlDecimal 对象允许您将精度和小数位数以及小数位传递给构造函数。我知道列的大小,所以在我们写入数据之前,我会检查每个输入,以便生成业务所需的输出。

在本例中,我们存储的是百分比,因此精度为 13,比例为 10。我有一个测试工具,我在下面将其压缩为 SO 的 unti 测试。此示例在 SqlDecimal 构造函数行上引发算术溢出错误:

    [TestMethod]
    public void TestDecimalFits()
    {
        decimal d = 10.3m;
        SqlDecimal sqlDecimal = new SqlDecimal(13, 10, d >= 0, Decimal.GetBits(d));
        Assert.AreEqual(d, sqlDecimal.Value);
    }

有人知道为什么会这样吗?

谢谢!

【问题讨论】:

  • 可以直接使用带十进制类型的SqlDecimal构造函数。喜欢这个SqlDecimal sqlDecimal = new SqlDecimal(d);
  • 我可以,但是它不能验证我在下面的评论中提到的尾随零问题。这会引发错误并且会起作用:SqlDecimal.ConvertToPrecScale(new SqlDecimal(value), precision, scale);

标签: c# sql-server type-conversion decimal


【解决方案1】:

我的猜测是Decimal.GetBits(d) 溢出了 Int32 返回类型。 尝试只做Int32 bits = Decimal.GetBits(d) 看看它是否会引发同样的错误。

【讨论】:

  • Decimal.GetBits 返回 int[],而不是 Int32
  • 值得一试,将其分解为自己的行仍然会导致 SqlDecimal 构造函数抛出异常。
【解决方案2】:

Decimal.GetBits 的返回与SqlDecimal 的构造函数参数不兼容。

Decimal.GetBits 返回一个表示十进制精确结构的数组,其中包括 96 位缩放整数值和 8 位指数(加上 1 个符号位和 27 个未使用位)。

您正在使用的 SqlDecimal constructor 采用一个 int 数组,该数组表示“提供新 SqlDecimal 值的 128 位无符号整数”。 - 不是那个十进制值的表示scale 参数确定小数的实际值。

因此,您实际上传递的值与构造函数期望的值不同。 .NET 十进制相当于 10.3m 是

0000000001100111-0000000000000000-0000000000000000-10000000000000000

其中1100111 是 103 的二进制等价物,1 是小数位数。

与该二进制值等效的整数长度超过 13 位,这就是为什么在将其传递给 SqlDecimal 构造函数时会发生溢出。

我不会玩弄比特,而是只使用原始的decimal 值,让 SQL 将其转换为正确的精度并自动缩放。

我正在尝试验证 c# 中的小数是否适合 db 列小数。

嗯,适合DECIMAL(13,10) 的最大值是999.9999999999,它远低于decimal 的最大值。所以不,您不能在DECIMAL(13,10) SQL 列中存储任何 C# 十进制值。

(从技术上讲,我认为您可以通过降低精度来存储9999999999999,但即使这样也远低于decimal 的最大值)。

【讨论】:

  • SqlDecimal 的构造函数接受一个 int 数组,而不是一个字节数组,但这可能是原因。只传入一个小数的问题在于,十进制 c# 小数的尾随零是灵活的,而在 sql server 中它们总是被填充,导致输出溢出问题。
  • 啊,好吧,所以我自己不弄乱这些位确实有效,我只需要在之后设置精度和比例。谢谢! SqlDecimal.ConvertToPrecScale(new SqlDecimal(value), precision, scale);
  • 可以,但是您不需要直接创建SqlDecimal。 SQL 提供程序应该为您隐式转换您的decimal 值。如果您有需要创建SqlDecimal的情况,那应该可以。
最近更新 更多