我正在进行地理计算,最终以纬度和经度存储在Geography::Point
对象中。
经度和纬度最多可以有7位数字(这也可以使精度达到11毫米,这足够了)。
问题是:如果某个字段的值不能正确存储在Double
中,则 MS SQL 会四舍五入到可以的最接近的数字,但是可以通过添加一堆数字。
=> ,例如5.9395772
存储为5.9395771999999996
由此产生的问题是[Position].ToString()
超出了该列允许的最大字符数(不,我不能增加该限制)。
由于我们正在处理纬度,经度,高度和精度,因此每个纬度和经度都有正好11个字符的空间:
String.Format(CultureInfo.InvariantCulture, "{0:##0.0######}", num)
我只是尝试Math.Round()
到6位数字,但是其他数字(例如6.098163
到6.0981629999999996
)也遇到了同样的问题。
我如何Math.Round
向最接近的7位有效位表示法表示?
编辑/添加
Public Function ToString_LatLon(ByVal num As Double) As String
num = Math.Round(num, 7, MidpointRounding.AwayFromZero)
Return String.Format(CultureInfo.InvariantCulture, "{0:##0.0######}", num)
End Function 'IN = 5.9395772, OUT = 5.9395772
上面的代码接收到Double
,并正确返回String
表示形式。我已经检查过了,这对于数字打扰也是正确的。
它通过我们使用的框架存储在SQL Server中。 我认为存储值时会出现问题
检索值时,在VB中出现错误,表示该值比框架允许的宽度宽(最多50个字符)。
如果我在SSMS中运行查询,则会发现例如POINT (X.0981629999999996 XX.664725 NULL 15602.707)
(51个字符,已停用)。
编辑2
我做了更多的研究和一些计算。似乎已存储的值5.9395772
转换为二进制并以5.9395771999999996
的形式返回,该值作为double存储在数据库中(在二进制Geography::Point
对象中,不用担心。)将二进制0 10000000001 0111110000100010000010000110100010000100010011011101
返回十进制,您会得到5.93957719999999955717839839053340256214141845703125
,但缩写为16位小数-而我希望将其缩写为7位小数。
解决方案:
讨论:
最后,经过大量的白板计算和冗长的讨论之后,我们选择了选项4 。从数据库中检索[Position].ToString()
(为此我们增加了字符串限制)之后,我们将其转换为已经进行的转换,并作为在任何地方使用它的附加步骤,将值四舍五入到所需数量小数位数。将值返回数据库时,我们再次将值四舍五入为小数位数,而不管数据库实际上是如何处理的。
本质上,这是选项2,但是在程序端而不是数据库端。
答案 0 :(得分:1)
这只是部分答案。
如果用有效位表示的意思是精确位表示,那么这是可能的。具有确切位表示形式的十进制数字为1 / 2、1 / 4、3 / 4、1 / 8、3 / 8、5 / 8、7 / 8、1 / 16、3 / 16,... >
面临的挑战是在以10为基数表示7位或更少的数字的两个幂之间进行表征,然后将任何以10为基数的数字四舍五入到这些数字中最接近的数字。
我发布此邮件是希望它可以使您更进一步地寻求解决方案。
答案 1 :(得分:0)
如果由于某种原因无法将数据类型更改为DECIMAL
,则每次需要该值时都必须将其转换为DECIMAL
。就这么简单。您可以在SQL Server端或VB.NET中进行此操作,但是需要DECIMAL
。 DOUBLEs
不精确。
顺便说一句,不是SQL Server通过添加一堆数字将其舍入到最接近的数字,而是由处理器完成。这就是为什么在另一台服务器上还原数据库后,您可能会得到稍微不同的DOUBLE
值的原因。
甚至从来没有想过将它们用作ID:我知道一个应用程序将包含时间戳记(FLOAT
的{{1}}值用作(几乎每个表的)主键的一部分。 。第10000条左右的记录不能直接通过其ID进行寻址,因为在发送查询的客户端和服务器上,该值有些不同,尽管该数字在客户端和服务器上的SSMS中看起来完全相同。