2016-12-16 139 views
0

我有一段代碼:TSQL數四捨五入問題

IF OBJECT_ID(N'dbo.rounding_testing') IS NOT NULL 
    DROP FUNCTION dbo.rounding_testing; 
GO 
CREATE FUNCTION dbo.rounding_testing 
(
    @value FLOAT, 
    @digit INT 
) 
RETURNS FLOAT 
BEGIN 
    DECLARE 
     @factor FLOAT, 
     @result FLOAT; 
    SELECT @factor = POWER(10, @digit); 

    SELECT @result = FLOOR(@value * @factor + 0.4); 

    RETURN @result; 
END; 
GO 

SELECT dbo.rounding_testing(5.7456, 3); 
SELECT FLOOR(5.7456 * 1000 + 0.4); 

的結果是:

5745 
5746 

我期待2 5746.我試着調試功能,並發現了一些有趣的行爲。以下是調試時我在Immediate Window中做的一些測試。

@factor 
1.000000000000000e+003 
@result 
5.745000000000000e+003 
@value 
5.745600000000000e+000 
@value*@factor 
5745.6 
@value*@factor+0.4 
5746 
floor(@value*@factor+0.4) 
5745 
floor(5746) 
5746 

任何人都可以幫忙解釋結果嗎?尤其是以下三行:

@value*@factor+0.4 
5746 
floor(@value*@factor+0.4) 
5745 
floor(5746) 
5746 

回答

1

在表達式FLOOR(5.7456 * 1000 + 0.4);是固定的,括號之間的部分首先計算。對於常量,數據類型是基於符號推斷的;對於5.7456即decimal(5,4); 1000是int;而0.4是decimal(1,1)。 5.7456 * 1000的推斷數據類型爲decimal(10,4);並且爲了充分表達,它是decimal(11,4)。這些都是精確的數字數據類型,因此您不會遇到任何舍入;確切地說,最終結果是5746.0000。所述FLOOR功能修剪部分和轉換爲decimal(11,0),返回5746.

在用戶定義的函數,則存儲輸入參數和中間結果在float數據類型(浮點數據)。此數據類型旨在用於近似數據,例如測量數據,您從儀器中讀取的數據已經是近似值。我已經在高中學到了儘可能多的數字,但把最後一個數字看作是微不足道的 - 我必須在所有計算中保留它,但根據我的測量精度將最終結果舍入到有效數字的位數。舍入確保最後數字中的不準確性不會影響最終結果。 浮點數據類型應該以相同的方式處理。

在內部,浮點數位以基數2數字系統表示。這意味着有一些數字在我們常用的base-10系統(如5.7456)中具有精確的表示形式,但是在base-2中有一個永無止境的小數部分。 (類似於例如三分之一,其可以完全以3爲基數表示,具有以10爲底的永不落幕小數部分:0.33333333333(等))。用於存儲float號碼的基數爲2的數字是有限的,因此必須在最後切斷 - 這會導致其上或下舍入一小部分。你可以看到這一點,如果你運行:

DECLARE @a float = 5.7456; 
SELECT CAST(@a AS decimal(19,16)); 

在這種情況下,很多基2位後切斷的效果是存儲的值比你把十進制值少0.0000000000000004如此之小。由於函數FLOOR函數的差異變成了一個巨大的效果,該函數完成它應該做的事情:向下舍入到最接近的整數。 (我見過很多人稱這是一個錯誤,它不是,它是有目的和有記錄的行爲,而且這裏的精確度損失既不差也不會比你存儲第三個精度時的精度損失更好在DECIMAL(7,6);這只是不太明顯,因爲我們都長大了習慣於以10爲基礎工作)

+0

你能花時間格式化嗎? –

+0

丹尼爾,我對這個頻道很陌生。現在好點了嗎? –

0

您的問題可以通過改變浮動到真實

IF OBJECT_ID(N'dbo.rounding_testing') IS NOT NULL 
    DROP FUNCTION dbo.rounding_testing; 
GO 
CREATE FUNCTION dbo.rounding_testing 
(
    @value REAL, 
    @digit INT 
) 
RETURNS REAL 
BEGIN 
    DECLARE 
     @factor REAL, 
     @result REAL; 
    SELECT @factor = POWER(10, @digit); 

    SELECT @result = FLOOR(@value * @factor + 0.4); 

    RETURN @result; 
END; 
GO 

SELECT dbo.rounding_testing(5.7456, 3); 
SELECT FLOOR(5.7456 * 1000 + 0.4); 
+0

爲什麼浮動不起作用? –