【问题标题】:How many unique values are there between 0 and 1 of a standard float?标准浮点数的 0 和 1 之间有多少个唯一值?
【发布时间】:2013-07-30 14:27:00
【问题描述】:

我猜这个问题的另一种表述方式是,使用仅介于 0 和 1 之间的 float 可以达到小数点几位?

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

但是如果我这样做:

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

它写出9.949219E-44

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

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

【问题讨论】:

标签: c# floating-point ieee-754


【解决方案1】:

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

这并不是您真正想要回答的问题,但答案是,不包括 01 本身,在此范围内有 2**23 - 1 次正常数和 126 * 2**23 正常数,对于总共127 * 2**23 - 1,或1,065,353,215

但请注意,这些数字均匀分布在01 之间的区间上。在从0f1f 的循环中使用1f / 1065353215f 的“增量”对您不起作用。

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

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

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

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

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

或等价于文字:

const float delta = 5.96046448e-8f;

这个增量的好处是,您通过循环遇到的所有值都可以在您的float 中完全表示。就在1f 之前(下),您将采取尽可能短的步骤来实现该规模。

【讨论】:

  • 我当时可能确实问错了问题。我在问一个浮点数,因为它是一个框架的一部分,所以我不能将它更改为十进制。看来我真正需要使用的只是:0.000001,但这会将我的潜在价值降低到太低。我可能不得不采取另一条路线,例如使用整数,然后在 0 和 1 之间进行规范化。
  • @MatthewMcGovern 0.00...01 形式的步骤不适用于像 float 这样的二进制类型。请参阅我最近的编辑,了解二进制“整数”的步骤。
  • “每个计算机科学家都应该知道的关于浮点运算的知识”(docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html#929) 完全按照它上面所说的那样做。
  • 如果你在循环中计算0.000001*i,而不是求和,你将只需要处理一个舍入误差,而不是累加。
  • @BlueRaja-DannyPflughoeft 好主意。还与 MatthewMcGovern 本人的上述评论有关,他谈到使用整数进行循环并“标准化”。
【解决方案2】:

它是 7 个 有效 位,也就是说,当用指数表示法写它时,你忽略了指数。

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

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

【讨论】:

  • 如果我使用0 <= x >= 1 的值使用0.00000000000000000000000000000000000000000001f 的步骤,我会遇到任何问题吗?
  • 那么,鉴于有效数字这一事实,我们能否确定 0 到 1 之间可能存在的不同值的确切数量?它会是可能的负指数数量的 10^7 倍吗?
  • 当他请求0.00000000000000000000000000000000000000000001f 并得到9.949219e-44f 时,他进入了Single 类型的subnormal 范围。在这种情况下,他的前 5 位或 7 位有效小数位不正确,因为该数字不正常。
  • 如果我数正确的话,正次正规数的个数是2**23 - 1,而严格小于1的正正规数个数是126 * 2**23。那么开区间从零到一的数字总数为127 * 2**23 - 1,或1,065,353,215
  • 这就是我现在粗略的知识崩溃的地方,但显然我的 2^30 上限是正确的,并且在这种情况下可能足够接近。我会把它留在这里,如果你想写一个事实正确的答案,而不需要任何猜测或近似值(这超出了我现在的知识范围),你可以这样做:-)
【解决方案3】:

我写了这个非常愚蠢的程序,它给出了答案1065353217,它确实只差 230 (1073741824)。如果您要查找所有数字不包括 0 和 1,请从该数字中减去 2。顺便说一下,最小的非零数字似乎是 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();
    }
}

【讨论】:

  • 在我对乔伊的回答的(更正)评论中,我得到1,065,353,215,所以我们同意。
  • 嗯,我一直在尝试使用如此小的值,但我正在努力实现我需要的东西。似乎任何乘法都会破坏准确性。此外,如果最小的数字是1.401298E-45,为什么不循环并添加1,065,353,215 次等于1?
  • @MatthewMcGovern -- 相邻浮点数之间的步长随它们的比例而变化,因此没有一个浮点值可以逐步击中它们。你到底想做什么?
  • @RussellBorogove:简而言之,2D Isometric 游戏中的假 Z 深度。我试图想出一个聪明的方法来计算每个对象的深度,因为在 X Y 和 Z 轴上可能有数千个,并且深度参数必须是 0 到 1 之间的浮点数。我可能会放弃这个想法并直接跳到 3D,因为它解决了这个问题,而且更即时。
  • @MatthewMcGovern 如果我理解正确,您应该能够假装量化是 2^23 的倒数。接近 1.0 这是正确的,接近 0.0 实际量化更精细。
【解决方案4】:

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

如果您想在计数中包含端点,请记住有两种编码为零。

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

【讨论】:

    【解决方案5】:

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

    附:代码执行可能需要 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
        }
    

    【讨论】:

      猜你喜欢
      • 2021-12-30
      • 1970-01-01
      • 1970-01-01
      • 2014-09-22
      • 1970-01-01
      • 2023-03-28
      • 2011-07-07
      相关资源
      最近更新 更多