【问题标题】:SQL 2012 bug for the ACOS functionACOS 函数的 SQL 2012 错误
【发布时间】:2012-04-17 09:40:36
【问题描述】:

我在 SQL Server 2012 ACOS 函数中遇到了这个错误:

declare @lat1 decimal(12,10), @lon1 decimal(12,10), @lat2 decimal(12,10), @lon2 decimal(12,10)
declare @dist float

select @lat1=51.1790825000, @lon1= 4.1590020000, @lat2= 51.1790825000, @lon2= 4.1590020000  
set @dist = SIN(RADIANS(@lat1)) * SIN(RADIANS(@lat2)) + COS(RADIANS(@lat1)) * COS(RADIANS(@lat2)) * COS(RADIANS(@lon1 - @lon2))

print @dist
print ACOS(1)
print ACOS(@dist)

最后一个打印函数给出“发生了无效的浮点运算”。这在 SQL Server 2008 中运行良好

弗拉德

【问题讨论】:

  • 这不是问题。如果您发现了错误,请将其发布到 connect
  • 同一台服务器有 2 个不同的实例或 2 台不同的机器?我在本地 PC 上的 SQL Server 2012 RC0 和 SQL Server 2008 R2 Express 上都出现错误。在联网的 SQL Server 2008 R2 上。所以这可能归结为硬件(这颗CPU)

标签: sql-server sql-server-2012


【解决方案1】:

您似乎正在尝试计算地球上两点之间的距离。使用内置的地理类型让您的生活更轻松。

DECLARE @lat1 DECIMAL(12, 10) ,
    @lon1 DECIMAL(12, 10) ,
    @lat2 DECIMAL(12, 10) ,
    @lon2 DECIMAL(12, 10)
DECLARE @dist FLOAT

SELECT  @lat1 = 51.1790825000 ,
        @lon1 = 4.1590020000 ,
        @lat2 = 51.1790825000 ,
        @lon2 = 4.1590020000

DECLARE @p1 GEOGRAPHY = GEOGRAPHY::Point(@lat1, @lon1, 4326) ,
    @p2 GEOGRAPHY = GEOGRAPHY::Point(@lat2, @lon2, 4326)

SELECT  @dist = @p1.STDistance(@p2)

【讨论】:

  • 超酷!干杯不需要罪恶 co tan 处理自己!但是这个'4326'是什么?
  • 好的,知道了。 STSrid 是一个整数,表示实例的空间参考标识符 (SRID)。
  • 虽然很方便,但这种方法比自己计算要慢得多。在我自己的测试中,我发现在 50 万行上进行数学运算大约需要 12 秒,而使用地理对象的时间要慢 3 倍,需要 36 秒。您的硬件不同,因此请自行测试,但请注意,如果考虑性能你应该考虑避免这种方法。
  • @mattmc3:也许吧。当然,我敢打赌,很多时间实际上都花在了动态创建地理实例上。在实践中,您可以对存储在数据库中的点执行一次,然后从那里运行地理方法。当然是 YMMV。
【解决方案2】:

使用

print ACOS(CASE WHEN @dist > 1 THEN 1 ELSE @dist END)

@dist 是一个float 数据类型,实际上由于舍入问题而略大于1,如下所示。

SELECT CAST(@dist AS BINARY(8)) AS [@dist], 
       CAST(CAST(1 AS FLOAT) AS BINARY(8)) AS [1]

返回

@dist              1
------------------ ------------------
0x010000000000F03F 0x000000000000F03F

010000000000F03F插入the IEEE converter here表明这大约是1.0000000000000002220446049250313080847263,可以从下面验证(返回Y

SELECT 
     CASE WHEN @dist between 1.0000000000000002220446049250313080847 AND 
                             1.0000000000000002220446049250313080848 
     THEN 'Y' ELSE 'N' END

【讨论】:

    【解决方案3】:

    acos() 函数的浮点值参数必须介于 -1 到 1 之间。因此

    FUNCTION [dbo].[Calculate_Distance]  
    (  
    @Lat1 float, @Long1 float, @Lat2 float, @Long2 float  
    )  
    RETURNS float  
    AS  
    BEGIN  
    
        DECLARE @acosValue float     
        DECLARE @R float  
        SET @R = 3958.7558657440545 -- mi       
        DECLARE @Distance float  
        Set @acosValue= cos( radians(@Lat1) ) * cos( radians( @Lat2 ) ) * cos( radians( @Long2 ) - radians(@Long1) ) + sin( radians(@Lat1) ) * sin( radians( @Lat2 ) ) ;
        IF @acosValue>1.0 
        Begin
        Set @acosValue=1.0;
         End
        IF @acosValue<-1.0 
        Begin
        Set @acosValue=-1.0;
         End
    
        SET @Distance = acos(@acosValue)
         * @R;  
    
        RETURN @Distance  
    
    END 
    GO
    

    试试这个

    【讨论】:

      【解决方案4】:

      正如其他答案所述,您有一个浮点问题,它会根据不同的 CPU 架构以不同的方式呈现。但是,有一个简单的方法可以解决您的问题:

      declare @dist float 需要改为declare @dist decimal(12, 10)

      【讨论】:

      • 遇到了同样的问题。转换为十进制 (16,15) 降低了精度,突然 SQL 无法区分 ACOS(1) 和无效的 ACOS(1.0000000000000001)!
      猜你喜欢
      • 2023-03-18
      • 2016-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-27
      相关资源
      最近更新 更多