一千萬個為什麽

搜索

查找具有相同內部表示的float/double的最小值/最大值

刷新浮點數(也 PDF ),IEEE-754和參與在轉換為字符串時進行浮點四舍五入的討論,讓我知道如何獲得給定浮點數的最大值和最小值二進制表示是相等的。

Disclaimer: for this discussion, I like to stick to 32 bit and 64 bit floating point as described by IEEE-754. I'm not interested in extended floating point (80-bits) or quads (128 bits IEEE-754-2008) or any other standard (IEEE-854).

Background: Computers are bad at representing 0.1 in binary representation. In C#, a float represents this as 3DCCCCCD internally (C# uses round-to-nearest) and a double as 3FB999999999999A. The same bit patterns are used for decimal 0.100000005 (float) and 0.1000000000000000124 (double), but not for 0.1000000000000000144 (double).

為方便起見,以下C#代碼給出了這些內部表示:

string GetHex(float f)
{
    return BitConverter.ToUInt32(BitConverter.GetBytes(f), 0).ToString("X");
}

string GetHex(double d)
{
    return BitConverter.ToUInt64(BitConverter.GetBytes(d), 0).ToString("X");
}

// float
Console.WriteLine(GetHex(0.1F));

// double 
Console.WriteLine(GetHex(0.1));

0.1 的情況下,不存在用相同位模式表示的小數,任何 0.99 ... 99 將產生不同的位表示(即,對於 0.999999937 的float在內部產生 3F7FFFFF )。

My question is simple: how can I find the lowest and highest decimal value for a given float (or double) that is internally stored in the same binary representation.

Why: (I know you'll ask) to find the error in rounding in .NET when it converts to a string and when it converts from a string, to find the internal exact value and to understand my own rounding errors better.

我的猜測是這樣的:取尾數,刪除其余部分,得到其確切值,得到一個(尾數位)更高,並計算平均值:低於該值的任何值將產生相同的位模式。我的主要問題是:如何獲得小數部分為整數(位操作它不是我最強大的資產)。 Jon Skeet的DoubleConverter 課程可能會有幫助。

最佳答案

解決問題的一種方法是在 L ast P中找到 ULP U nit的大小</強烈的>花邊,你的浮點數。簡化一點,這是給定浮點數與下一個更大數之間的距離。再次,簡化一點,給定一個可表示的浮點值x,其值在(x - 1/2 ulp)和(x + 1/2 ulp)之間的任何十進制字符串將在轉換為浮動時舍入為x點值。

技巧是(x +/- 1/2 ulp)不是一個可表示的浮點數,所以實際計算它的值需要使用更寬的浮點類型(如果有的話)或任意寬度的大十進制或類似的類型來進行計算。

你如何找到ulp的大小?一種相對簡單的方法大致上是你所建議的,這裏寫的是C-ish偽代碼,因為我不知道C#:

float absX = absoluteValue(x);
uint32_t bitPattern = getRepresentationOfFloat(absx);
bitPattern++;
float nextFloatNumber = getFloatFromRepresentation(bitPattern);
float ulpOfX = (nextFloatNumber - absX);

This works because adding one to the bit pattern of x exactly corresponds to adding one ulp to the value of x. No floating-point rounding occurs in the subtraction because the values involved are so close (in particular, there is a theorem of ieee-754 floating-point arithmetic that if two numbers x and y satisfy y/2 <= x <= 2y, then x - y is computed exactly). The only caveats here are:

  1. 如果x碰巧是最大的有限浮點數,這將不起作用(它會返回 inf ,這顯然是錯誤的)。
  2. 如果您的平臺不能正確支持逐漸下溢(例如嵌入式設備以flush-to-zero模式運行),則對於非常小的x值不適用。

這聽起來像你不可能在這些情況下,所以這應該適用於你的目的。

現在你已經知道x的ulp是什麽了,你可以找到四舍五入到x的值的間隔。您可以精確計算浮點數ulp(x)/ 2,因為浮點除以2是精確的(再次,禁止下溢)。然後,只需計算x +/- ulp(x)/ 2的值就可以獲得適合的較大浮點類型(如果您對 float 感興趣, double 將可用)或以大十進制類型顯示,並且您有間隔。

我通過這個解釋做了一些簡化的假設。如果你需要這個確實拼寫出來,請留下評論,當我有機會時,我會擴展有點模糊的部分。


One other note the following statement in your question:

在0.1的情況下,沒有更低的   表示的十進制數   具有相同的位模式

是不正確的。你恰巧正在查看錯誤的值(0.999999 ...而不是0.099999 ...... - 一個簡單的拼寫錯誤)。

轉載註明原文: 查找具有相同內部表示的float/double的最小值/最大值