我正在尝试将MS#2005中的C#double存储为浮点数。但是,似乎有一系列小数字是有效的双打但不是有效的浮点数。例如,当我尝试存储值1e-320时,我得到错误“提供的值不是float类型的有效实例”。
这与SQL浮点数的文档一致,它的最小值为2.23e-308 http://msdn.microsoft.com/en-us/library/ms173773.aspx
以及C#双打的文档,其最小值为5.0e-324 http://msdn.microsoft.com/en-us/library/678hzkk9(VS.71).aspx
所以我的问题是处理这个问题的最佳方法是什么?我可以将double转换为一个有效的SQL float吗?
答案 0 :(得分:3)
选项:
你不能做什么:
编辑:
SQL Sever只是不明白这个数字:无论使用什么样的客户端库或c#数据类型或技巧,它都不能作为数字存储在SQL Server中。
答案 1 :(得分:2)
如果你想保持双倍的全部价值,你可能需要使用@gbn的一个解决方案。
如果你只是喜欢使用SQL float的范围,如果它超出该范围则舍入为零,如果值超出范围,则创建一个舍入为零的辅助方法应该是相当简单的。 SQL float。
private static SqlParameter CreateDoubleParameter(string parameterName, double value)
{
const double SqlFloatEpsilon = 2.23e-308;
SqlParameter parameter = new SqlParameter(parameterName, SqlDbType.Float);
parameter.Value = value > -SqlFloatEpsilon && value < SqlFloatEpsilon ? 0d : value;
return parameter;
}
答案 2 :(得分:0)
您可以将数据类型更改为decimal
,也可以添加第二列以保存存储值应移位的小数位数,以产生原始值。也就是说,使用此方案存储0.000456将导致存储4.56和5,而34567.89可以表示为3.456789和-4(减号表示向左移位)。
然而,第二种选择可能会导致精度的逐渐下降。
答案 3 :(得分:-1)
您可以使用SqlDouble类型。这是来自System.Data.SqlTypes命名空间,
为SQL Server中的本机数据类型提供类。这些类为.NET Framework公共语言运行库(CLR)提供的数据类型提供了更安全,更快速的替代方法。使用此命名空间中的类有助于防止因精度损失而导致的类型转换错误。由于其他数据类型在后台与 SqlTypes 进行转换,因此在此命名空间中显式创建和使用对象也会产生更快的代码。
编辑:显然SqlDouble的范围是-1.79E +308到1.79E +308,这与事物的正指数方面的双倍相同,并且不接受负指数差异考虑在内。从范围检查的角度来看,它看起来不会有用。作为旁注,该命名空间中的其他类型(如SqlDateTime)看起来可能实际上对范围检查有用,但不是SqlDouble。
答案 4 :(得分:-1)
从根本上说,尝试在32位值中存储64位值会导致精度损失,编译器会警告您这个问题。你真正需要问的是“我真的需要那么精确吗?”
如果你不需要精度,那么“float output = Convert.ToSingle(inputAsDouble);”我会做的 - 它只会四舍五入到最接近的可表示单精度值。
如果确实需要精度,但仍需要适合32位的值,则必须以某种方式约束范围。例如,如果您知道您的值总是在-1e-319到1e-319的范围内,那么您可以使用定点数学在存储的32位值和您需要使用的双值之间进行转换计算。这样返回的double值将无法表示您范围内的所有可能的数值,但是您将在该有限范围内具有32位粒度,这实际上非常准确。
例如,您可以创建一个辅助类,如:
struct FixedDouble
{
int storage;
public FixedDouble(double input)
{
storage = DoubleToStorage(input);
}
public double AsDouble
{
get
{
return StorageToDouble(storage);
}
}
const double RANGE = 1e-319;
public static int DoubleToStorage(double input)
{
Debug.Assert(input <= RANGE);
Debug.Assert(input >= -RANGE);
double rescaledValue = (input / RANGE) * int.MaxValue;
return (int)rescaledValue;
}
public static double StorageToDouble(int input)
{
double rescaledValue = ((double)input / (double)int.MaxValue) * RANGE;
return rescaledValue;
}
}
这段代码可能无法正常工作,因为我很快就把它搞砸了,但是这个想法就在那里 - 基本上你牺牲了双重提供给你的全部范围,而是在两个数字之间选择一个固定的粒度,并且32位值允许您在这两个数字之间的数字线上定义一个点。