Floating point 在给定范围内,有多少值可以用浮点表示?

Floating point 在给定范围内,有多少值可以用浮点表示?,floating-point,precision,Floating Point,Precision,直觉告诉我,因为32位可以表示固定数量的不同值,所以对于任何给定的范围,浮点可以表示固定数量的值。这是真的吗?处理转换的方式是否会损失能够表示的值的数量 假设我在[10301035]范围内选择一个数字。显然,我能在这个范围内获得的精度是有限的,但与更合理的范围(如[0.0,1000.0])相比,在这个范围内可以表示的值的数量是否有任何差异?这个答案假设float映射到IEEE-754(2008)标准指定的二进制32类型。对于规范化的binary32操作数,即[2-126,2128),因为存储的有

直觉告诉我,因为32位可以表示固定数量的不同值,所以对于任何给定的范围,浮点可以表示固定数量的值。这是真的吗?处理转换的方式是否会损失能够表示的值的数量


假设我在[10301035]范围内选择一个数字。显然,我能在这个范围内获得的精度是有限的,但与更合理的范围(如[0.0,1000.0])相比,在这个范围内可以表示的值的数量是否有任何差异?

这个答案假设
float
映射到IEEE-754(2008)标准指定的
二进制32
类型。对于规范化的
binary32
操作数,即[2-126,2128),因为存储的有效位的数量是23,所以每个二进制码总是正好有223个编码。在一般情况下确定
binary32
编码的数量有点棘手,例如由于舍入效应:并非所有的十次幂都可以精确表示。在二进制码中,这也会造成差异起点和终点都已确定,我们需要考虑[0,2-126]中的次正常值

但首先,我们可以估计[10301035]中的编码数量与[10-2103]中的编码数量大致相同,因此区间[0103]将包含比区间[10301035]更多的
binary32
数字

<> P>一种建立精确计数的懒惰方法是在给定间隔内计算编码数。C和C++标准数学库提供了函数“代码> NEXTAFFTF < /C> >将给定的<代码> Bial32 32 <代码>操作数递增或递减到其所指示的方向上的最接近的邻居。mes我们能够在指定的时间间隔内完成此操作。使用此方法的ISO-C99程序如下所示。在现代硬件上,只需几秒钟即可给出所需的答案:

#包括
#包括
#包括
#包括
/*在关闭的间隔[开始,停止]中计算二进制32个数字*/
无效计数(浮动开始、浮动停止)
{
浮动x;
uint32_t计数;
计数=0;
x=开始;

虽然(x这是为了提供信息,但它可以用来提供更容易使用的信息,例如提供计数的代码、不同范围的样本或讨论,但我没有时间,希望保留到目前为止的信息

对于IEEE-754基本32位二进制浮点,小于或等于非负x的非负表示值的数目N(x)为:

  • 223•254如果2128≤ 十,
  • 223•(楼层(log2x)+127)+楼层(x/2楼层(log2x)−23)−223+1如果2−126≤ x<2128
  • 楼层(x/2)−126−如果x<2,则为23)+1−126
因此a 说明:

  • 在第一种情况下,223•254是可表示的非负有限值的数量,每个指数值223,包括次正常值和零
  • 在第二种情况下,223•(floor(log2x)+127)对于x之下的每个二进制码是223,包括次正常值和零。除此之外,我们在x的二进制码中添加小于x的数字,我们通过计算x的24位整数有效位(向下舍入)得到floor(x/2floor(log2x)−23)然后减去223−1计算从第一个正常有效位(223)到x的有效位(含223)的有效位
  • 在第三种情况下,次正常有效位的间隔为2−126−23,所以我们只计算整个间隔,包括终点
在给定范围内,有多少值可以用浮点表示

…由于32位可以表示固定数量的不同值,
float
可以表示任何给定范围的固定数量的值。这是真的吗

是-正确。在整个范围内,大约可以表示232个不同的值

处理转换的方式是否会损失能够表示的值的数量

.
float
不定义如何将其他数值表示转换为或从
float
printf()、scanf()、atof()、strtof()、(float)某些整数、(某些整数类型)一些浮点数和编译器本身都执行转换。C对转换必须达到的程度没有明确规定。高质量的库和编译器应尽可能地发挥最佳性能。对于源代码或“字符串”数字,如
“1.2345”
,有无限多个可能值映射到大约232个不同的值。是的,会发生丢失

…在范围[10301035]…与更合理的范围(如[0.01000.0])相比,在该范围内可以表示的值的数量是否有任何差异


是的。
float
值不是线性的。在[10301035]之间,大约有[1.030,1.035]或[1.030e-3,1.035e-3]之间的
float
的差异。大约25%的
float
值在
[0.0…1.0]
范围内,所以
[0.0,1000.0]
中的值比
多出很多倍[10301035]

这里的代码计算所有有限范围内可在
float
中表示的值的数量。它需要IEEE-754算法。我根据它改编

这有两种将浮点数转换为编码的方法(一种是通过复制位,另一种是通过数学操作)。之后,距离计算相当简单(必须调整负值,然后距离只是一个减法)

#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
/*仅使用uint32_t集的高位定义值。这也是
浮点编码-0。
*/
静态常数32\u t
there are 1148846081 binary32 numbers in [0.00000000e+000, 1.00000000e+003]
there are 139864311 binary32 numbers in [9.99999978e-003, 1.00000000e+003]
there are 139468867 binary32 numbers in [1.00000002e+030, 1.00000004e+035]
#include <float.h>
#include <inttypes.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <tgmath.h>


/*  Define a value with only the high bit of a uint32_t set.  This is also the
    encoding of floating-point -0.
*/
static const uint32_t HighBit = UINT32_MAX ^ UINT32_MAX>>1;


//  Return the encoding of a floating-point number by copying its bits.
static uint32_t EncodingBits(float x)
{
    uint32_t result;
    memcpy(&result, &x, sizeof result);
    return result;
}


//  Return the encoding of a floating-point number by using math.
static uint32_t EncodingMath(float x)
{
    static const int SignificandBits = FLT_MANT_DIG;
    static const int MinimumExponent = FLT_MIN_EXP;

    //  Encode the high bit.
    uint32_t result = signbit(x) ? HighBit : 0;

    //  If the value is zero, the remaining bits are zero, so we are done.
    if (x == 0) return result;

    /*  The C library provides a little-known routine to split a floating-point
        number into a significand and an exponent.  Note that this produces a
        normalized significand, not the actual significand encoding.  Notably,
        it brings significands of subnormals up to at least 1/2.  We will
        adjust for that below.  Also, this routine normalizes to [1/2, 1),
        whereas IEEE 754 is usually expressed with [1, 2), but that does not
        bother us.
    */
    int xe;
    float xf = frexp(fabs(x), &xe);

    //  Test whether the number is subnormal.
    if (xe < MinimumExponent)
    {
        /*  For a subnormal value, the exponent encoding is zero, so we only
            have to insert the significand bits.  This scales the significand
            so that its low bit is scaled to the 1 position and then inserts it
            into the encoding.
        */
        result |= (uint32_t) ldexp(xf, xe - MinimumExponent + SignificandBits);
    }
    else
    {
        /*  For a normal value, the significand is encoded without its leading
            bit.  So we subtract .5 to remove that bit and then scale the
            significand so its low bit is scaled to the 1 position.
        */
        result |= (uint32_t) ldexp(xf - .5, SignificandBits);

        /*  The exponent is encoded with a bias of (in C++'s terminology)
            MinimumExponent - 1.  So we subtract that to get the exponent
            encoding and then shift it to the position of the exponent field.
            Then we insert it into the encoding.
        */
        result |= ((uint32_t) xe - MinimumExponent + 1) << (SignificandBits-1);
    }

    return result;
}


/*  Return the encoding of a floating-point number.  For illustration, we
    get the encoding with two different methods and compare the results.
*/
static uint32_t Encoding(float x)
{
    uint32_t xb = EncodingBits(x);
    uint32_t xm = EncodingMath(x);

    if (xb != xm)
    {
        fprintf(stderr, "Internal error encoding %.99g.\n", x);
        fprintf(stderr, "\tEncodingBits says %#" PRIx32 ".\n", xb);
        fprintf(stderr, "\tEncodingMath says %#" PRIx32 ".\n", xm);
        exit(EXIT_FAILURE);
    }

    return xb;
}


/*  Return the distance from a to b as the number of values representable in
    float from one to the other.  b must be greater than or equal to a.  0 is
    counted only once.
*/
static uint32_t Distance(float a, float b)
{
    uint32_t ae = Encoding(a);
    uint32_t be = Encoding(b);

    /*  For represented values from +0 to infinity, the IEEE 754 binary
        floating-points are in ascending order and are consecutive.  So we can
        simply subtract two encodings to get the number of representable values
        between them (including one endpoint but not the other).

        Unfortunately, the negative numbers are not adjacent and run the other
        direction.  To deal with this, if the number is negative, we transform
        its encoding by subtracting from the encoding of -0.  This gives us a
        consecutive sequence of encodings from the greatest magnitude finite
        negative number to the greatest finite number, in ascending order
        except for wrapping at the maximum uint32_t value.

        Note that this also maps the encoding of -0 to 0 (the encoding of +0),
        so the two zeroes become one point, so they are counted only once.
    */
    if (HighBit & ae) ae = HighBit - ae;
    if (HighBit & be) be = HighBit - be;

    //  Return the distance between the two transformed encodings.
    return be - ae;
}


static void Try(float a, float b)
{
    printf("[%.99g, %.99g] contains %" PRIu32 " representable values.\n",
        a, b, Distance(a, b) + 1);
}


int main(void)
{
    if (sizeof(float) != sizeof(uint32_t))
    {
        fprintf(stderr, "Error, uint32_t must be the same size as float.\n");
        exit(EXIT_FAILURE);
    }

    /*  Prepare some test values:  smallest positive (subnormal) value, largest
        subnormal value, smallest normal value.
    */
    float S1 = FLT_TRUE_MIN;
    float N1 = FLT_MIN;
    float S2 = N1 - S1;

    //  Test 0 <= a <= b.
    Try( 0,  0);
    Try( 0, S1);
    Try( 0, S2);
    Try( 0, N1);
    Try( 0, 1./3);
    Try(S1, S1);
    Try(S1, S2);
    Try(S1, N1);
    Try(S1, 1./3);
    Try(S2, S2);
    Try(S2, N1);
    Try(S2, 1./3);
    Try(N1, N1);
    Try(N1, 1./3);

    //  Test a <= b <= 0.
    Try(-0., -0.);
    Try(-S1, -0.);
    Try(-S2, -0.);
    Try(-N1, -0.);
    Try(-1./3, -0.);
    Try(-S1, -S1);
    Try(-S2, -S1);
    Try(-N1, -S1);
    Try(-1./3, -S1);
    Try(-S2, -S2);
    Try(-N1, -S2);
    Try(-1./3, -S2);
    Try(-N1, -N1);
    Try(-1./3, -N1);

    //  Test a <= 0 <= b.
    Try(-0., +0.);
    Try(-0., S1);
    Try(-0., S2);
    Try(-0., N1);
    Try(-0., 1./3);
    Try(-S1, +0.);
    Try(-S1, S1);
    Try(-S1, S2);
    Try(-S1, N1);
    Try(-S1, 1./3);
    Try(-S2, +0.);
    Try(-S2, S1);
    Try(-S2, S2);
    Try(-S2, N1);
    Try(-S2, 1./3);
    Try(-N1, +0.);
    Try(-N1, S1);
    Try(-N1, S2);
    Try(-N1, N1);
    Try(-1./3, 1./3);
    Try(-1./3, +0.);
    Try(-1./3, S1);
    Try(-1./3, S2);
    Try(-1./3, N1);
    Try(-1./3, 1./3);

    return 0;
}
/* count the binary32 numbers in the closed half-open interval [start, stop[ */
int distance (float start, float stop)
{
    return *(reinterpret_cast<int *>(&stop)) - *(reinterpret_cast<int *>(&start));
}