我想將浮點數的最小可能值添加到浮點數。因此,舉例來說,我想這樣做是爲了得到1.0 +可能的最小浮動:將最小可能浮點數添加到浮點數
float result = 1.0f + std::numeric_limits<float>::min();
但這樣做,我得到下面的結果後:我使用Visual Studio 2015年
(result > 1.0f) == false
(result == 1.0f) == true
。爲什麼會發生這種情況?我能做些什麼來解決它?
我想將浮點數的最小可能值添加到浮點數。因此,舉例來說,我想這樣做是爲了得到1.0 +可能的最小浮動:將最小可能浮點數添加到浮點數
float result = 1.0f + std::numeric_limits<float>::min();
但這樣做,我得到下面的結果後:我使用Visual Studio 2015年
(result > 1.0f) == false
(result == 1.0f) == true
。爲什麼會發生這種情況?我能做些什麼來解決它?
如果您想要1後面的下一個可表示值,那麼將會有一個名爲std::nextafter
的函數,該函數來自<cmath>
標頭。
float result = std::nextafter(1.0f, 2.0f);
它從第一個參數開始向第二個參數的方向返回下一個可表示的值。所以,如果你想找到低於1的下一個值,你可以這樣做:
float result = std::nextafter(1.0f, 0.0f);
添加的最小正表示值1不起作用,因爲1和一個可表示的值之間的差大於大0與下一個可表示值之間的差值。
'std :: numeric_limits
IIRC,大約一半的浮點比特模式代表一個小於'1.0'的數字。指數字段的範圍或多或少以'0'爲中心(代表尾數的'2^0 = 1.0'乘數),在考慮了編碼方式中的偏差後,將FP位模式排序爲整數實際上工作。請參閱Bruce Dawson關於浮點奇怪內容的優秀系列文章,包括[關於代表性的這一篇](https://randomascii.wordpress.com/2012/01/11/tricks-with-the-floating-point-format /) –
請參閱[本文](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/)以獲取該系列FP文章中的目錄。 –
min
是(歸一化形式)float可以假定的最小非零值,即大約2 -126(-126是浮點的最小允許指數);現在,如果你總結爲1,你仍然會得到1,因爲float
只有23位尾數,所以這樣一個小的變化不能用這麼大的數字表示(你需要一個126位的尾數才能看到1的變化總和)。
的最小可能的改變爲1,相反,是epsilon
(所謂機器最小),這實際上是2 -23 - 因爲它影響尾數的最後一個比特。
'std :: numeric_limits
@ user2357112:我應該在我的個人資料中添加一個警告,說明「我對浮點進行的任何討論都是無視非規範化的數字,這是醜陋的野獸最好忽略的」:-) –
缺少低於正常值的是更醜陋的。如果可用的子正常減去兩個不相等的數字,將始終給出非零的答案。沒有低於正常值的情況下,它不會。 – plugwash
您正在觀察的「問題」是由於浮點運算的非常自然的。
在FP中,精度取決於規模;圍繞價值1.0
精度是不夠的,能夠1.0
和1.0+min_representable
其中min_representable
可能是最小的值大於零(即使我們只考慮最小規格化數,std::numeric_limits<float>::min()
更大的區分。最小的非正規的幅度的另一少量訂單小)。
例如與雙精度64位IEEE754浮點數字,周圍的x=10000000000000000
規模(10 )這是不可能的x
和x+1
之間進行區分。
事實上,帶有刻度的分辨率的變化是非常之所以命名爲「浮點」,因爲小數點「浮動」。一個固定點表示反而會有一個固定的分辨率(例如16個二進制數字低於你的單位精度爲1/65536〜0.00001)。
例如在IEEE754 32位浮點格式有一個比特的標誌,用於指數的8位和用於尾數31位:
的最小值eps
,使得1.0f + eps != 1.0f
可用作預定義的常數,如FLT_EPSILON
或std::numeric_limits<float>::epsilon
。另請參閱machine epsilon on Wikipedia,其中討論了epsilon與舍入錯誤的關係。
I.e. epsilon是您在此期待的最小值,在添加到1.0時有所作爲。
這個(對於1.0以外的數字)更通用的版本在尾數(尾數)被稱爲1個單位。請參閱維基百科的ULP article。
我想這個問題是植根於「浮點」(或者只是「浮點」)這個詞,用於「計算機中的非整數」而不考慮(或者甚至不知道)實際的浮動特性(即精度取決於按比例)。 –
正確。如果有人要做這樣的事情,那麼花一些時間研究浮點背後的概念是個好主意。有一些「令人驚訝的」效果可能發生,特別是對於不知情的用戶。 –
**'eps'是FLT_MIN **的錯誤名稱。 'eps'是'FLOAT_EPSILON'的縮寫,它是[添加到'1.0'時有所不同的最小數字](https://en.wikipedia.org/wiki/Machine_epsilon#Variant_definitions)。它的最後一個位置(尾數)爲1,單位爲'1.0'(見[ulp](https://en.wikipedia.org/wiki/Unit_in_the_last_place))。你所描述的是epsilon和1 ULP的概念,但問題是「eps =大於零的最小可能值」。 –
要將浮點值增加/減少最小可能值,請使用nextafter
朝向+/- infinity()
。
如果您只是使用next_after(x,std::numeric_limits::max())
,如果x
是無窮大,結果將會出錯。
#include <iostream>
#include <limits>
#include <cmath>
template<typename T>
T next_above(const T& v){
return std::nextafter(1.0,std::numeric_limits<T>::infinity()) ;
}
template<typename T>
T next_below(const T& v){
return std::nextafter(1.0,-std::numeric_limits<T>::infinity()) ;
}
int main(){
std::cout << next_below(1.0) - 1.0<< std::endl; // gives eps
std::cout << next_above(1.0) - 1.0<< std::endl; // gives ~ -eps/2
// Note:
std::cout << std::nextafter(std::numeric_limits<double>::infinity(),
std::numeric_limits<double>::infinity()) << std::endl; // gives inf
std::cout << std::nextafter(std::numeric_limits<double>::infinity(),
std::numeric_limits<double>::max()) << std::endl; // gives 1.79769e+308
}
你爲什麼感到驚訝?您正在添加min,而不是epsilon。 –
我沒有意識到有一個區別!我曾假設他們總是等同的。謝謝,這很有幫助。 – Squidy
@Matteo答案?這個問題我沒有真正的理由。 –