【问题标题】:Find the float just below a value找到一个值正下方的浮点数
【发布时间】:2013-01-11 12:29:57
【问题描述】:

假设我有一个浮点数 X。我想找到小于 X 并且可以无损存储在浮点数中的最大数。

IIRC IEEE 标准说,您可以通过将浮点数的位转换为 int 表示,减一,然后再转换回浮点数来做到这一点。

编辑:这对于非 NaN 或 inf 的正数是正确的。对于负数,您必须添加。有关更多信息,请参阅 Rawling's answer。)

要在表示之间进行更改,我只知道 C# 的 (cast) 运算符,它会截断。这不是我想要的。

有没有办法在 C# 中做到这一点?

【问题讨论】:

  • 您在寻找Math.Floor吗?
  • 你想达到什么目的?
  • 你的问题很难理解。写下您拥有的一些示例值以及您希望该示例成为什么值。
  • 通过阅读问题,我假设 OP 不是要找到可以由低于特定值的浮点数据类型准确表示的最大数字。
  • 你能从 C# 调用 C 代码吗? C99 有一个函数 nextafterf (float x, float y),它返回 x 之后沿 y 方向的下一个可表示的 'float'。

标签: c# floating-point


【解决方案1】:

您可以通过以下方式将float 简单地转换为int,对其进行更改,然后再将其转换回float

float myFloat = 10.3f;
// Get the bytes making up the float
byte[] bytes = BitConverter.GetBytes(myFloat);
// Make an int out of them
int myInt = BitConverter.ToInt32(bytes, 0);
// Change it
myInt--;
// Get the bytes making up the int
bytes = BitConverter.GetBytes(myInt);
// Make a float out of them
myFloat = BitConverter.ToSingle(bytes, 0);
// gives 10.2999992 or so

BitConverter 甚至内置了 64 位等效项:

double myDouble = 10.3;
long myLong = BitConverter.DoubleToInt64Bits(myDouble);
myLong--;
myDouble = BitConverter.Int64BitsToDouble(myLong); // gives 10.2999999...

但是,正如Peter Ruderman 指出的那样,基础int 的简单递减并不能可靠地为您提供下一个最小的float

特别是对于负数,您需要递增整数以使浮点数更负。对于float 零,下一个最小的int 实际上对应于NaN,所以你需要一个特殊情况。

以下是我拼凑起来的几个函数,它们通常可以应对这些情况;它看起来也很明智地在大数和正/负无穷大之间移动!我使用了不安全的转换来减少代码长度,但如果你愿意,你可以坚持上面的字节转换:

static unsafe float Increment(float f)
{
    int val = *(int*)&f;
    if (f > 0)
        val++;
    else if (f < 0)
        val--;
    else if (f == 0)
        return float.Epsilon;
    return *(float*)&val;
}
static unsafe float Decrement(float f)
{
    int val = *(int*)&f;
    if (f > 0)
        val--;
    else if (f < 0)
        val++;
    else if (f == 0)
        return -float.Epsilon; // thanks to Sebastian Negraszus
    return *(float*)&val;
}

正如Jeppe 指出的那样,您可能还想要

  • 每个函数都以if (float.IsNaN(f)) return f; 开头,这样您就不会意外地增加或减少NaN 并给出一个数字。
  • 还可以考虑对照float.PositiveInfinity.NegativeInfinity 检查输入,因为从数学上讲,这些值在递增/递减时应该保持不变。

【讨论】:

  • 这并不适用于所有情况——尤其是当初始浮点数为 0 或负数时。
  • 为零,您可以返回-float.Epsilon
  • @SebastianNegraszus Brilliant,谢谢,我正在找那个。
  • 如果你增加MaxValue,你会得到PositiveInfinity,这很好。但如果你增加它,你也会进入NaN 范围。也可以增加或减少NaN 域的out,您可能还想处理?位布局见en.wikipedia.org/wiki/Single-precision_floating-point_format
  • 假设您从正零 (0f) 开始并保持递增(无条件递增整数)。然后首先你会遇到正的次正规数。然后你会遍历所有正常的正数,第一个是1.17549435E-38,最后一个是3.40282347E+38(最大值)。然后是PositiveInfinity。所有带有未设置符号位的NaN。然后毕竟它们是负零。然后是负次正规数(最接近零的最先​​出现),然后是负正规数,然后是负无穷大,“负”NaNs,然后再次回到正零。
【解决方案2】:

为此有一个库例程,nexttowardf(x, -INFINITY);

如果您想使用自己的代码执行此操作,您可以在本机执行(在 IEEE 754 浮点运算中,无需访问浮点编码或组件),如下所示(在 C 中)。

提供了两种版本,一种在每种情况下都使用次正规值(在某些处理器上可能很慢),另一种仅在输入很小时才使用次正规值(但它有一个分支)。 +INFINITY 不支持作为输入,尽管可以通过简单的测试添加支持。这是为double 编写的,但float 的更改很简单。

如果定义了CompileMain,它还包括一个测试程序。

#include <float.h>
#include <math.h>


/*  Return the next floating-point value before the finite value q.

    This was inspired by Algorithm 3.5 in Siegfried M. Rump, Takeshi Ogita, and
    Shin'ichi Oishi, "Accurate Floating-Point Summation", _Technical Report
    05.12_, Faculty for Information and Communication Sciences, Hamburg
    University of Technology, November 13, 2005.
*/
double NextBefore(double q)
{
    // SmallestPositive is the smallest positive floating-point number.
    static const double SmallestPositive = DBL_EPSILON * DBL_MIN;

    /*  Scale is .625 ULP, so multiplying it by any significand in [1, 2)
        yields something in [.625 ULP, 1.25 ULP].
    */
    static const double Scale = 0.625 * DBL_EPSILON;

#if 0
    /*  This version has a branch but uses subnormal values only if q is so
        small that q * Scale is subnormal.
    */
    double increment = fabs(q)*Scale;

    if (0. == increment)
        return q - SmallestPositive;

    return q - increment;
#else
    /*  This version uses a subnormal, SmallestPositive, in each case.
        This might cause poor performance on some processors.
    */
    return q - fmax(SmallestPositive, fabs(q)*Scale);
#endif
}


#if defined CompileMain


#include <stdio.h>
#include <stdlib.h>


#define NumberOf(a) (sizeof (a) / sizeof *(a))


int main(void)
{
    int status = EXIT_SUCCESS;

    static const struct { double in, out; } cases[] =
    {
        { -INFINITY,                -INFINITY                },
        { -0x1.fffffffffffffp1023,  -INFINITY                },
        { -0x1.ffffffffffffep1023,  -0x1.fffffffffffffp1023  },
        { -0x1.ffffffffffffdp1023,  -0x1.ffffffffffffep1023  },
        { -0x1.ffffffffffffcp1023,  -0x1.ffffffffffffdp1023  },
        { -0x1.0000000000003p1023,  -0x1.0000000000004p1023  },
        { -0x1.0000000000002p1023,  -0x1.0000000000003p1023  },
        { -0x1.0000000000001p1023,  -0x1.0000000000002p1023  },
        { -0x1.0000000000000p1023,  -0x1.0000000000001p1023  },

        { -0x1.fffffffffffffp1022,  -0x1.0000000000000p1023  },

        { -0x1.fffffffffffffp1,     -0x1.0000000000000p2     },
        { -0x1.ffffffffffffep1,     -0x1.fffffffffffffp1     },
        { -0x1.ffffffffffffdp1,     -0x1.ffffffffffffep1     },
        { -0x1.ffffffffffffcp1,     -0x1.ffffffffffffdp1     },
        { -0x1.0000000000003p1,     -0x1.0000000000004p1     },
        { -0x1.0000000000002p1,     -0x1.0000000000003p1     },
        { -0x1.0000000000001p1,     -0x1.0000000000002p1     },
        { -0x1.0000000000000p1,     -0x1.0000000000001p1     },

        { -0x1.fffffffffffffp-1022, -0x1.0000000000000p-1021 },
        { -0x1.ffffffffffffep-1022, -0x1.fffffffffffffp-1022 },
        { -0x1.ffffffffffffdp-1022, -0x1.ffffffffffffep-1022 },
        { -0x1.ffffffffffffcp-1022, -0x1.ffffffffffffdp-1022 },
        { -0x1.0000000000003p-1022, -0x1.0000000000004p-1022 },
        { -0x1.0000000000002p-1022, -0x1.0000000000003p-1022 },
        { -0x1.0000000000001p-1022, -0x1.0000000000002p-1022 },
        { -0x1.0000000000000p-1022, -0x1.0000000000001p-1022 },

        { -0x0.fffffffffffffp-1022, -0x1.0000000000000p-1022 },
        { -0x0.ffffffffffffep-1022, -0x0.fffffffffffffp-1022 },
        { -0x0.ffffffffffffdp-1022, -0x0.ffffffffffffep-1022 },
        { -0x0.ffffffffffffcp-1022, -0x0.ffffffffffffdp-1022 },
        { -0x0.0000000000003p-1022, -0x0.0000000000004p-1022 },
        { -0x0.0000000000002p-1022, -0x0.0000000000003p-1022 },
        { -0x0.0000000000001p-1022, -0x0.0000000000002p-1022 },
        { -0x0.0000000000000p-1022, -0x0.0000000000001p-1022 },

        { +0x1.fffffffffffffp1023,  +0x1.ffffffffffffep1023  },
        { +0x1.ffffffffffffep1023,  +0x1.ffffffffffffdp1023  },
        { +0x1.ffffffffffffdp1023,  +0x1.ffffffffffffcp1023  },
        { +0x1.0000000000004p1023,  +0x1.0000000000003p1023  },
        { +0x1.0000000000003p1023,  +0x1.0000000000002p1023  },
        { +0x1.0000000000002p1023,  +0x1.0000000000001p1023  },
        { +0x1.0000000000001p1023,  +0x1.0000000000000p1023  },

        { +0x1.0000000000000p1023,  +0x1.fffffffffffffp1022  },

        { +0x1.0000000000000p2,     +0x1.fffffffffffffp1     },
        { +0x1.fffffffffffffp1,     +0x1.ffffffffffffep1     },
        { +0x1.ffffffffffffep1,     +0x1.ffffffffffffdp1     },
        { +0x1.ffffffffffffdp1,     +0x1.ffffffffffffcp1     },
        { +0x1.0000000000004p1,     +0x1.0000000000003p1     },
        { +0x1.0000000000003p1,     +0x1.0000000000002p1     },
        { +0x1.0000000000002p1,     +0x1.0000000000001p1     },
        { +0x1.0000000000001p1,     +0x1.0000000000000p1     },

        { +0x1.0000000000000p-1021, +0x1.fffffffffffffp-1022 },
        { +0x1.fffffffffffffp-1022, +0x1.ffffffffffffep-1022 },
        { +0x1.ffffffffffffep-1022, +0x1.ffffffffffffdp-1022 },
        { +0x1.ffffffffffffdp-1022, +0x1.ffffffffffffcp-1022 },
        { +0x1.0000000000004p-1022, +0x1.0000000000003p-1022 },
        { +0x1.0000000000003p-1022, +0x1.0000000000002p-1022 },
        { +0x1.0000000000002p-1022, +0x1.0000000000001p-1022 },
        { +0x1.0000000000001p-1022, +0x1.0000000000000p-1022 },

        { +0x1.0000000000000p-1022, +0x0.fffffffffffffp-1022 },
        { +0x0.fffffffffffffp-1022, +0x0.ffffffffffffep-1022 },
        { +0x0.ffffffffffffep-1022, +0x0.ffffffffffffdp-1022 },
        { +0x0.ffffffffffffdp-1022, +0x0.ffffffffffffcp-1022 },
        { +0x0.0000000000004p-1022, +0x0.0000000000003p-1022 },
        { +0x0.0000000000003p-1022, +0x0.0000000000002p-1022 },
        { +0x0.0000000000002p-1022, +0x0.0000000000001p-1022 },
        { +0x0.0000000000001p-1022, +0x0.0000000000000p-1022 },
    };

    for (int i = 0; i < NumberOf(cases); ++i)
    {
        double in = cases[i].in, expected = cases[i].out;
        double observed = NextBefore(in);
        printf("NextBefore(%a) = %a.\n", in, observed);
        if (! (observed == expected))
        {
            printf("\tError, expected %a.\n", expected);
            status = EXIT_FAILURE;
        }
    }

    return status;
}


#endif  // defined CompileMain

【讨论】:

  • 这似乎是一个很好的答案,但我正在寻找 C# 解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多