问题
I run into this bug in SQL Server 2012 ACOS function:
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)
The last print function gives an "An invalid floating point operation occurred." This works fine in SQL Server 2008
Vlad
回答1:
It looks like you're trying to calculate the distance between two points on the Earth. Make your life easier and use the built-in geography type.
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)
回答2:
Use
print ACOS(CASE WHEN @dist > 1 THEN 1 ELSE @dist END)
@dist is a float datatype and is actually slightly more than 1 due to rounding issues as can be seen from below.
SELECT CAST(@dist AS BINARY(8)) AS [@dist],
CAST(CAST(1 AS FLOAT) AS BINARY(8)) AS [1]
Returns
@dist 1
------------------ ------------------
0x010000000000F03F 0x000000000000F03F
Plugging 010000000000F03F into the IEEE converter here shows that this is approximately 1.0000000000000002220446049250313080847263 which can be verified from the below (returns Y)
SELECT
CASE WHEN @dist between 1.0000000000000002220446049250313080847 AND
1.0000000000000002220446049250313080848
THEN 'Y' ELSE 'N' END
回答3:
The float value argument for acos() function has to be between -1 to 1 range.. Hence
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
Try this
回答4:
As the other answers have stated, you have a floating point issue, which will present itself differently based on different CPU architectures. However, there's a simple one-liner that fixes your issue:
declare @dist float needs changed to declare @dist decimal(12, 10).
来源:https://stackoverflow.com/questions/10188781/sql-2012-bug-for-the-acos-function