会重复规范化IEEE浮点向量吗?

时间:2018-12-07 04:41:49

标签: floating-point

如果我采用一个(非零)浮点向量(一个(x, y, z)向量)并将其归一化为单位长度,是否可以再次对其进行归一化以保证返回相同的结果?

3 个答案:

答案 0 :(得分:4)

我不了解文献的相关结果。一项快速测试表明,即使仔细地执行了标准化操作(例如,通过更高精度的算术进行计算),对3D矢量进行两次标准化也会经常导致标准化向量与重新标准化向量之间的微小差异。我使用下面的ISO-C99程序进行此快速测试,并使用x64平台的编译器的“严格”浮点设置(icl /fp:strict)对其进行了编译。

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>

#define USE_HYPOT  (1)

// Fixes via: Greg Rose, KISS: A Bit Too Simple. http://eprint.iacr.org/2011/007
static uint32_t z=362436069,w=521288629,jsr=362436069,jcong=123456789;
#define znew (z=36969*(z&0xffff)+(z>>16))
#define wnew (w=18000*(w&0xffff)+(w>>16))
#define MWC  ((znew<<16)+wnew)
#define SHR3 (jsr^=(jsr<<13),jsr^=(jsr>>17),jsr^=(jsr<<5)) /* 2^32-1 */
#define CONG (jcong=69069*jcong+13579)                     /* 2^32 */
#define KISS ((MWC^CONG)+SHR3)

float uint32_as_float (uint32_t a)
{
    float r;
    memcpy (&r, &a, sizeof(r));
    return r;
}

void normalize_3d (float *a, float *b, float *c)
{
#if USE_HYPOT
    double l = hypot (hypot ((double)a[0], (double)b[0]), (double)c[0]);
#else // USE_HYPOT
    double l = sqrt ((double)a[0]*a[0] + (double)b[0]*b[0] + (double)c[0]*c[0]);
#endif // USE_HYPOT
    *a = (float)((double)a[0] / l);
    *b = (float)((double)b[0] / l);
    *c = (float)((double)c[0] / l);
}

int main (void)
{
    float a, aa, aaa, b, bb, bbb, c, cc, ccc;
    do {
        /* generate random vector */
        do {
            a = uint32_as_float (KISS & ~0x80000000u);
        } while (isnanf (a) || (a > 0x1.0p126f) || (a < 0x1.0p-126f));
        do {
            b = uint32_as_float (KISS & ~0x80000000u);
        } while (isnanf (b) || (b > 0x1.0p126f) || (b < 0x1.0p-126f));
        do {
            c = uint32_as_float (KISS & ~0x80000000u);
        } while (isnanf (c) || (c > 0x1.0p126f) || (c < 0x1.0p-126f));

        /* normalize vector once */
        aa = a; bb = b; cc = c;
        normalize_3d (&aa, &bb, &cc);

        /* re-normalize normalized vector */
        aaa = aa; bbb = bb; ccc = cc;
        normalize_3d (&aaa, &bbb, &ccc);

        /* check whether normalized vector is equal to re-normalized one */
        if ((aa != aaa) || (bb != bbb) || (cc != ccc)) {
            printf ("norm = (%15.6a, %15.6a, %15.6a)  re-norm = (%15.6a, %15.6a, %15.6a)\n", aa, bb, cc, aaa, bbb, ccc);
        }
    } while (1);
    return EXIT_SUCCESS;
}

答案 1 :(得分:2)

不,不能保证。假设所有单精度浮点数都用于归一化计算,则下面提供了一个反例:

#include <stdio.h>
#include <math.h>

void normalize(float a, float b, float c, float *outA, float *outB, float *outC)
{
    float norm = sqrtf (a*a+b*b+c*c);
    *outA = a / norm;
    *outB = b / norm;
    *outC = c / norm;
}

int main(void)
{
    float a =  -4.33681e-19;
    float b = -1.326157e-23;
    float c = 2.8502696e-20;

    float a1, b1, c1;
    normalize(a, b, c, &a1, &b1, &c1);

    float a2, b2, c2;
    normalize(a1, b1, c1, &a2, &b2, &c2);

    printf("Original        : (%a, %a, %a)\n", a,  b,  c);
    printf("Normalized      : (%a, %a, %a)\n", a1, b1, c1);
    printf("Twice normalized: (%a, %a, %a)\n", a2, b2, c2);
}

此打印:

Original        : (-0x1.000006p-61, -0x1.00841ep-76, 0x1.0d334ap-65)
Normalized      : (-0x1.fee5d8p-1, -0x1.ffed76p-16, 0x1.0c9eeap-4)
Twice normalized: (-0x1.fee5d4p-1, -0x1.ffed72p-16, 0x1.0c9ee8p-4)

即使使用双精度进行归一化,您仍然可以找到结果发生变化的反例。

答案 2 :(得分:0)

我玩过这个游戏,看来您可以得到各种各样的行为。对于以下示例,我使用了

hypotf( a, hypotf( b,c ))

计算长度。 下表显示了三个坐标,然后显示了长度。 有时您最终会在两个值之间产生振荡:

    -0.000891105     0.402476907     0.915429652     0.999999881
    -0.000891105     0.402476966     0.915429771     0.999999881
    -0.000891105     0.402476907     0.915429652     1.000000119
    -0.000891105     0.402476966     0.915429771     0.999999881
    -0.000891105     0.402476907     0.915429652     1.000000119
    -0.000891105     0.402476966     0.915429771     0.999999881
    -0.000891105     0.402476907     0.915429652     1.000000119
    -0.000891105     0.402476966     0.915429771     0.999999881
    -0.000891105     0.402476907     0.915429652     1.000000119
    -0.000891105     0.402476966     0.915429771     0.999999881
    -0.000891105     0.402476907     0.915429652     1.000000119

有时候您会得到更复杂的行为:

 0.659812868     0.745291650     0.095850535     0.999999940
 0.659812927     0.745291710     0.095850542     0.999999940
 0.659812868     0.745291650     0.095850527     1.000000119
 0.659812927     0.745291710     0.095850535     0.999999940
 0.659812868     0.745291650     0.095850520     1.000000119
 0.659812927     0.745291710     0.095850527     0.999999940
 0.659812868     0.745291650     0.095850512     1.000000119
 0.659812927     0.745291710     0.095850520     0.999999940
 0.659812868     0.745291650     0.095850505     1.000000119
 0.659812927     0.745291710     0.095850512     0.999999940
 0.659812868     0.745291650     0.095850497     1.000000119
 0.659812927     0.745291710     0.095850505     0.999999940
 0.659812868     0.745291650     0.095850490     1.000000119
 0.659812927     0.745291710     0.095850497     0.999999940
 0.659812868     0.745291650     0.095850483     1.000000119
 0.659812927     0.745291710     0.095850490     0.999999940
 0.659812868     0.745291650     0.095850475     1.000000119
 0.659812927     0.745291710     0.095850483     0.999999940
 0.659812927     0.745291710     0.095850483     1.000000000
 0.659812927     0.745291710     0.095850483     1.000000000
 0.659812927     0.745291710     0.095850483     1.000000000