【问题标题】:Floating point problem in SQL Server 2005SQL Server 2005 中的浮点问题
【发布时间】:2010-08-03 20:51:47
【问题描述】:

下面的代码用于计算两个城市之间的里程。在这种情况下,它是从缅因州雅茅斯到缅因州雅茅斯的距离 - 显然为零 - 这意味着雅茅斯 X 英里范围内的城市的结果应该包括雅茅斯本身。

问题是雅茅斯的纬度和经度似乎引起了某种浮点问题(我在其他城市没有看到这种情况):

DECLARE @fromlong FLOAT, @fromlat FLOAT, @tolong FLOAT, @tolat FLOAT, @test FLOAT

SET @fromlong = 43.8219
SET @fromlat = -70.1758
SET @tolong = 43.8219
SET @tolat = -70.1758
SET @test = SIN(@fromlong / ( 180 / PI() )) * SIN(@tolong / ( 180 / PI() )) + COS(@fromlong / ( 180 / PI() )) * COS(@tolong / ( 180 / PI() )) * COS(@fromlat / ( 180 / PI() ) - @tolat / ( 180 / PI() ))

PRINT @test /*** Displays "1" ***/

SELECT 3963.0 * ACOS(@test) /*** Displays "a domain error has occurred" ***/

首先,这是一个 SQL Server 错误吗?

其次,我该怎么做才能绕过它?我知道在上面的例子中我可以有一些IF @test > 1 的逻辑,但是这个例子是从嵌入在网络应用程序中的查询中提取的(不是我的选择),所以我需要修复查询,即修复计算,而不是求助如果可能的话到 TSQL,并且不扭曲任何其他返回值。有什么想法吗?

【问题讨论】:

  • 如果需要精确值,为什么要浮动?
  • 执行代码时是否应该出错?我没有得到一个只是0的结果。
  • 很好 - 我回去查看了数据库,发现我使用的是 SSMS 2008,但数据库是 2005。我会相应地更改帖子。
  • 我的机器上也没有出现错误(SQL2005 或 SQL2008 都没有)。如果我手动执行SELECT ACOS(1.00000000000000000000001),我确实会收到该错误。那么你可以改变什么来解决这个问题?你能把它改成ACOS(CASE WHEN @test > 1 THEN 1 ELSE @test END)吗?能不能实现自己的ACOS函数?
  • 如果您进行计算,则永远不应该使用浮点数据类型,它们是估计值而不是精确值。这可能会导致您的问题,但如果不是,则仍然需要修复,因为浮点数可能会出现巨大的舍入问题。

标签: sql sql-server-2005 floating-point


【解决方案1】:

cmets 的共识似乎是使用 FLOAT。我在示例中只使用了 FLOAT,因为这是从中读取纬度/经度的列的数据类型,但这似乎是问题的核心。由于我无法更改列本身的数据类型,因此最简单的解决方案是更改结果计算的数据类型,这似乎在所有情况下都可以正常工作:

SELECT 3963.0 * ACOS(CONVERT(DECIMAL(10, 6), @test))

我意识到将计算保留为浮点数可能会导致小的舍入误差,但对于此应用程序来说它们很小是可以接受的。感谢所有评论的人。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-01
    • 1970-01-01
    • 2011-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多