2011-11-02 36 views
3

假設我有一些代碼,如:我可以信任ceil()的結果的實數轉換嗎?

float a, b = ...; // both positive 
int s1 = ceil(sqrt(a/b)); 
int s2 = ceil(sqrt(a/b)) + 0.1; 

有沒有可能是s1 != s2?我的關注是當a/b是一個完美的廣場。例如,可能是a=100.0b=4.0,那麼ceil的輸出應該是5.00000,但是如果代之以是4.99999

相似問題:有沒有機會100.0/4.0評估說5.00001,然後ceil將它舍入到6.00000

我寧願這樣做在整數數學,但sqrt有點螺絲計劃。

編輯:關於如何更好地實現這一點的建議,也將不勝感激!該ab值是整數值,所以實際的代碼更像是:ceil(sqrt(float(a)/b))

編輯:基於levis501的回答,我想我會做到這一點:

float a, b = ...; // both positive 
int s = sqrt(a/b); 
while (s*s*b < a) ++s; 

謝謝大家!

+2

這些計算是什麼?例如,你可以將'sqrt(x) GManNickG

+1

不幸的是,即使「a/b」是一個完美的正方形,'s1!= s2'也是很可能的。浮點運算是非常不準確的,所以你獲得'5.00000'(或者任何你想要的數字)的機會都很渺茫。 – ssube

+0

@peachykeen即使我們正在處理整數數字? '5.0'可以完美地表示爲一個浮點數,'100.0'和'20.0'也是如此。 – glglgl

回答

2

你可能想爲你的情況寫一個明確的函數。例如爲:

/* return the smallest positive integer whose square is at least x */ 
int isqrt(double x) { 
    int y1 = ceil(sqrt(x)); 
    int y2 = y1 - 1; 
    if ((y2 * y2) >= x) return y2; 
    return y1; 
} 

這將處理奇數情況下的比率的平方根a/bdouble的精度內。

+0

我喜歡這種方法。它證明是正確的,不必知道或依賴浮點實現細節。 – Dan

-1

是的,這完全有可能是s1 != s2。但是,爲什麼這是一個問題? 這似乎很自然,s1 != (s1 + 0.1)

順便說一句,如果你希望有5.00001四捨五入到5.00000而不是6.00000,使用rint代替ceil


並回答實際問題(在您的評論) - 你可以使用sqrt得到一個起點,然後隨便找個使用整數運算正確的廣場。

int min_dimension_greater_than(int items, int buckets) 
{ 
    double target = double(items)/buckets; 
    int min_square = ceil(target); 
    int dim = floor(sqrt(target)); 
    int square = dim * dim; 
    while (square < min_square) { 
     seed += 1; 
     square = dim * dim; 
    } 
    return dim; 
} 

是的,這可以改善很多,它只是一個快速的草圖。

+2

s1和s2都是整數。鑄造截斷小數點 – levis501

+1

請注意,s1,s2是整數。我會研究rint,不知道它是否可用於我需要支持的所有平臺。 – Dan

+0

@Dan:如果你沒有'rint',那麼'floor(x + 0.5f)'會給出相同的結果。 –

6

我不認爲這是可能的。不管sqrt(a/b)值的,它所產生的是,我們使用作爲一些值N:

int s1 = ceil(N); 
int s2 = ceil(N) + 0.1; 

由於小區總是產生的整數值(雖然表示爲雙),我們將始終有一些值X,爲其中第一個產生X.0,第二個產生X.1。轉換爲int將始終截斷.1,所以兩者都將導致X

如果X太大以至於X.1溢出了double的範圍,看起來似乎會有一個異常。我不明白這可能是可能的。除了接近於0(其中溢出不是問題),數字的平方根總是比輸入數字小。因此,在ceil(N)+0.1可能溢出之前,作爲sqrt(a/b)中的輸入的a/b必須已經溢出。

+0

我同意我們應該期待信任'ceil',但我無法找到C標準中的哪裏(如果有的話),它實際上是在討論'math.h'中函數的準確性。假設0.5ulp的準確度,'ceil' *具有*返回一個整數,並且執行失敗將是非常不合理的。但是我們知道,'cos'不能保證是0.5ulp的準確值(或者'sqrt',我認爲這可以回答第二個問題),那麼爲什麼'ceil'應該是?除了因爲明顯的實現是屏蔽尾數的一些位(如果那些位不是0,加1),我的意思是。 –

+0

我想這取決於我們是否讀「計算最小整數值不小於x」意思是「保證返回的值是一個整數,等於......」或者意思是「確切的數學結果是...,但返回的價值受到通常不準確的影響「。 –

+0

@SteveJessop:實際上,你唯一一次遇到問題的機會是64位int。對於典型的32位int和具有53位尾數的double,你在做任何困難轉換double到精確的整數值之前都會溢出int。 –

1

浮點數的平等確實是一個問題,但恕我直言,不是如果我們處理整數。

如果你有100.0/4.0的情況,它應該完全評估爲25.0,因爲25.0可以精確地表示爲浮點數,例如與之相反。 25.1

-2

s1將始終等於s2。

C和C++標準對數學例程的準確性沒有太多的說明。從字面上看,標準是不可能實現的,因爲C標準說sqrt(x)返回x的平方根,但是二的平方根不能精確地用浮點數表示。

具有良好的性能實現例程總是返回正確舍入的結果(舍入到最接近的模式,這意味着結果是可表示的浮點數最接近的確切結果,有利於解決關係低零位)是一個很難研究的問題。好的數學庫的目標精度小於1 ULP(因此返回兩個最接近的可表示數字中的一個),也許稍微多於.5 ULP。 (ULP是最小精度的單位,低位的值在指數字段中給出了特定的值。)一些數學庫可能會比這更差。您將不得不詢問供應商或查看文檔以獲取更多信息。

所以sqrt可能會稍微偏離。如果確切的平方根是一個整數(在整數可精確表示浮點範圍內),並且庫保證錯誤小於1 ULP,那麼sqrt的結果必須完全正確,因爲除了確切的結果是至少1 ULP了。同樣,如果庫保證誤差小於1 ULP,則ceil必須返回精確結果,同樣因爲精確結果是可表示的,並且任何其他結果至少會有1 ULP。此外,ceil的性質使我可以期望任何合理的數學庫總是返回一個整數,即使庫的其餘部分不是高質量的。對於溢出情況,如果ceil(x)超出了所有整數都可以精確表示的範圍,那麼ceil(x)+ .1比其他任何可表示的數字更接近ceil(x),所以在執行浮點標準(IEEE 754)的任何系統中,向ceil(x)添加.1的舍入結果應該是ceil(x)。假設您處於默認的四捨五入模式,這是舍入到最近的。可以將舍入模式更改爲round-toward-infinity,這可能會導致ceil(x)+。1爲比ceil(x)更高的整數。

相關問題