Ошибка SQL 2012 для функции ACOS

Я столкнулся с этой ошибкой в ​​​​функции ACOS SQL Server 2012:

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

Влад


person vlad.pitaru    schedule 17.04.2012    source источник
comment
Это не вопрос. Если вы обнаружили ошибку, опубликуйте ее на connect.   -  person DaveShaw    schedule 17.04.2012
comment
Один и тот же сервер с двумя разными экземплярами или двумя разными машинами? Я получаю сообщение об ошибке как на SQL Server 2012 RC0, так и на SQL Server 2008 R2 Express на моем локальном ПК. А на сетевом SQL Server 2008 R2. Так что это сводится к аппаратному обеспечению (же центральному процессору), вероятно   -  person gbn    schedule 17.04.2012


Ответы (4)


Похоже, вы пытаетесь рассчитать расстояние между двумя точками на Земле. Упростите себе жизнь и используйте встроенный тип географии.

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)
person Ben Thul    schedule 17.04.2012
comment
очень круто ! ура, нет необходимости в грехе, потому что загар держит себя в руках! но что это за «4326»? - person Zia; 29.11.2013
comment
хорошо понял. STSrid — это целое число, представляющее идентификатор пространственной привязки (SRID) экземпляра. - person Zia; 29.11.2013
comment
Несмотря на удобство, этот метод значительно медленнее, чем самостоятельное вычисление. В моем собственном тестировании я обнаружил, что выполнение математических вычислений для полумиллиона строк заняло около 12 секунд по сравнению с использованием объекта geography, который был в 3 раза медленнее, используя 36. Ваше оборудование отличается, поэтому проверьте себя, но имейте в виду, что если производительность является соображением вам следует подумать об избегании этого метода. - person mattmc3; 31.12.2013
comment
@mattmc3: Возможно. Конечно, я готов поспорить, что много времени было потрачено на создание экземпляров geography на лету. На практике это то, что вы можете сделать один раз для точек, которые вы храните в своей базе данных, а затем запустить оттуда методы географии. ЮММВ, конечно. - person Ben Thul; 02.01.2014

Использовать

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 в преобразователь IEEE здесь показывает, что это примерно 1.0000000000000002220446049250313080847263, что может быть проверено снизу (возвращает Y)

SELECT 
     CASE WHEN @dist between 1.0000000000000002220446049250313080847 AND 
                             1.0000000000000002220446049250313080848 
     THEN 'Y' ELSE 'N' END
person Martin Smith    schedule 17.04.2012

Аргумент значения с плавающей запятой для функции 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

Попробуй это

person Raj    schedule 13.08.2012

Как указывалось в других ответах, у вас есть проблема с плавающей запятой, которая будет проявляться по-разному в зависимости от архитектуры ЦП. Однако есть простой однострочный код, который решает вашу проблему:

declare @dist float необходимо изменить на declare @dist decimal(12, 10).

person mattmc3    schedule 31.12.2013
comment
Столкнулся с той же проблемой. Приведение к десятичному (16,15) уменьшило точность, и внезапно у SQL не было проблем с различием между ACOS (1) и недопустимым ACOS (1.00000000000000001)! - person Tyler W. Cox; 06.12.2018