28

我想另一种表达这个问题的方式是你可以使用小数点到多少位float仅在 0 和 1 之间的小数位?

我试图通过查看MSDN来解决它。其中表示精度为 7 位。我认为这意味着它只能跟踪0.0000001.

但是,如果我这样做:

float test = 0.00000000000000000000000000000000000000000001f;
Console.WriteLine(test);

它写出9.949219E-44

如果我再添加零,它将输出0.

我很确定我在这里遗漏了一些东西,因为这种精确度似乎大错特错。主要是因为浮点数的大小为 32 位,并且在该精度级别上仅从 0-1 包含 1e+44 个可能的数字...

4

5 回答 5

31

标准浮点数的 0 和 1 之间有多少个唯一值?

这不是您真正想要回答的问题,但答案是,不包括它们本身0,在这个范围内12**23 - 1次正规数和正规126 * 2**23数,总共127 * 2**23 - 1, 或1,065,353,215.

但请注意,这些数字并非0均匀分布在和之间的区间上1。在 from to1f / 1065353215f循环中使用“delta”对您不起作用。0f1f

如果您想以(十进制)形式 0.00...01 的等长步长从 0.0 步进到 1.0,也许您应该使用decimal而不是float. 它将完全代表这样的数字。

如果您坚持使用float,请尝试使用0.000001(比您的建议值大十倍),但请注意,当使用不可表示的数字执行非常多的加法时,可能会产生错误。

另请注意:在一些“域”中,您甚至无法计算 a 的前七位有效十进制数字float。例如,尝试将值保存到0.000986f变量中(确保优化不会将值保存在“更宽”的存储位置)并写出该变量。前七位数字与resp不同。. 如果您想使用十进制扩展为“短”的数字,您可以再次使用。0.000987ffloat0.00098600000.0009870000decimal

编辑:如果您可以为循环使用“二进制”步骤,请尝试:

float delta = (float)Math.Pow(2, -24);

或等效地作为文字:

const float delta = 5.96046448e-8f;

这个 delta 的好处是,您通过循环遇到的所有值都可以在您的float. 就在 (under) 之前1f,您将针对该数量级采取尽可能最短的步骤。

于 2013-07-30T15:19:55.723 回答
18

它是 7位有效数字,也就是说,用指数表示法编写它时,您会忽略指数。

0.0000000000000000001234567 的有效位数与 12345670000000000000 相同,只是指数不同。这就是允许浮点数存储非常小的和非常大的数字的魔力。

至于float在 (0, 1) 中究竟有多少个可能的数字,我现在不能确切地说出来。你有一个 23 位尾数,所以它有 2 23种可能的状态。然后是一个 8 位指数,如果我没记错的话,大约一半的可能值将导致 0 到 1 之间的数字。这应该让您在该范围内有大约 2 23 + 7 = 2 30 个可能的值. 如果有任何可能是上限而不是确切值。我需要查阅有关更详细信息的文档才能准确了解(并且可能重新考虑上面可能会遗漏几点的数学)。

于 2013-07-30T14:30:20.690 回答
13

I wrote this very silly program, and it gives the answer 1065353217, which is indeed just shy of 230 (1073741824). Subtract 2 from that number if you were looking for all the numbers not including 0 and 1. By the way, the smallest non-zero number appears to be 1.401298E-45.

class Program
{
    public unsafe static int Search()
    {
        int i = 0;
        float* f = (float*)&i;
        while (*f >= 0 && *f <= 1)
        {
            f = (float*)&i;
            i++;
        }

        return i;
    }

    static void Main(string[] args)
    {
        Console.WriteLine(Search());
        Console.ReadLine();
    }
}
于 2013-07-30T14:50:40.850 回答
10

正浮点值的排序与其编码相同。 0.0f0x000000001.0f0x3f800000。所以有些0x3f800000 - 1浮点值严格介于两者之间,即 1,065,353,215。

如果要将端点包括在计数中,请记住有两种编码为零。

还要记住,浮点值不是均匀间隔的。1.0f和下一个较小的数字之间的差是,2**-240.0f下一个较大的数字之间的差是2**-149。如果您想以统一的步长将浮点数从 0 增加到 1,则可以使用的最小步长为2**-24.

于 2013-07-30T15:28:15.293 回答
0

由于浮点类型有 4 个字节数据,我们可以使用下面的代码检查所有 4 个字节(0-255)的所有可能变体,并计算其中有多少在 [0,1] 范围内 - 包括在内
所以答案将是1065353218

PS 代码执行可能需要 2-3 分钟,具体取决于 pc

public static void Main()
    {
        long count = 0;
        //byte checking sub/method
        void CheckThisBytes(byte ii, byte jj, byte kk, byte ll)
        {
            var data = new[] {ii, jj, kk, ll};
            var f = BitConverter.ToSingle(data, 0);
            //is f in range ?
            if (f >= 0.0 && f <= 1.0)
            {
                count++;
            }
        }
        const int max = 255;
        // generate all possible cases 
        for (var i = 0; i <= max; i++)
        {
            for (var j = 0; j <= max; j++)
            {
                for (var k = 0; k <=max; k++)
                {
                    for (var l = 0; l <= max; l++)
                    {
                        //check if current float is in range
                        CheckThisBytes((byte) i, (byte) j, (byte) k, (byte) l);
                    }
                }
            }
        }
        Console.WriteLine("\n Count:" + count);
        Console.ReadLine();
        //result will be  1065353218
    }
于 2019-09-30T15:39:02.570 回答