问题描述
||
我正在解决一个涉及以相当高的精度进行许多sql十进制乘法的问题。
最近的一个错误促使我进行了一些研究,并且我遇到了一篇关于小数点的精度和小数位数的令人um目结舌的文章(http://msdn.microsoft.com/en-us/library/ms190476.aspx)计算结果。
据我了解,为了避免截断或意外舍入,有时必须将操作数和可能的中间计算转换为可以起作用并提供所需结果的大小。
每个操作数都以十进制(19,5)的形式提供给我。由于我们支持如此广泛的价值观,因此我无能为力。
我的问题:是否可以将“即时”将这些十进制(19,5)值的类型减少/转换为将保留该值的最小十进制类型(精度和小数位数)?
例如,我可以在将变量(@Cw)用于乘法之前将其强制转换为十进制(4,1)“即时”吗?
DECLARE @Cw decimal(19,5)
SET @Cw = 113.5
--Some type of CAST,i.e.
--CAST(@Cw as DECIMAL(GetActualPrecision(@Cw),GetActualScale(@Cw)))
-–should show 4 instead of 19
SELECT sql_VARIANT_PROPERTY(@NewCw,\'Precision\')
–-should show 1 instead of 5
SELECT sql_VARIANT_PROPERTY(@NewCw,\'Scale\')
谢谢!
解决方法
没有
没有动态的CAST:比例和精度是静态的:“固定点”
如果您想使用任意精度,请使用float,它当然是“浮点”
我们主要使用小数,没有任何问题。
如果您的复杂数学无法很好地发挥作用,那么我会考虑在SQL中使用CLR或在具有更好库的客户端代码中使用CLR
, 我遇到了同样的问题,并使用CLR解决了该问题。以下C#函数将十进制类型“打包”到尽可能小的精度和小数位数。
internal static SqlDecimal PackPrecScale(SqlDecimal d)
{
//Remove leading zeros
var s = d.ToString();
var l = s.Length;
var indexofperiod = s.IndexOf(\'.\');
//Remove trailing zeros
if (indexofperiod != -1)
{
while (s[l - 1] == \'0\') l--;
if (s[l - 1] == \'.\')
{
l--;
indexofperiod = -1;
}
}
var precision = 6;
var scale = 0;
if (l > 0)
{
precision = l;
if (s[0] == \'-\') precision--;
if (indexofperiod != -1)
{
precision--;
scale = l - indexofperiod - 1;
}
if (precision < 6) precision = 6;
}
return SqlDecimal.ConvertToPrecScale(d,precision,scale);
}