5

有没有人有方便的代码片段将 IEEE 754 转换double为紧随其后的 (resp.superior) float而不改变或假设关于 FPU 当前舍入模式的任何事情

注意:这个约束可能意味着根本不使用 FPU。我希望在这些条件下最简单的方法是读取 64 位长的双精度位并使用它。

为简单起见,您可以假设您选择的字节序,并且可以通过d以下联合字段获得所讨论的双精度:

union double_bits
{
  long i;
  double d;
};

我会尝试自己做,但我确信我会为非规范化或负数引入难以注意到的错误。

4

3 回答 3

3

为了比重新组合尾数和指数位更准确地完成这项工作,请检查一下:

http://www.mathworks.com/matlabcentral/fileexchange/23173

问候

于 2010-01-06T10:01:20.080 回答
3

我认为以下方法可行,但我将首先陈述我的假设:

  • 浮点数在您的实现中以 IEEE-754 格式存储,
  • 没有溢出,
  • nextafterf()有空(在 C99 中指定)。

此外,这种方法很可能不是很有效。

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

int main(int argc, char *argv[])
{
    /* Change to non-zero for superior, otherwise inferior */
    int superior = 0;

    /* double value to convert */
    double d = 0.1;

    float f;
    double tmp = d;

    if (argc > 1)
        d = strtod(argv[1], NULL);

    /* First, get an approximation of the double value */
    f = d;

    /* Now, convert that back to double */
    tmp = f;

    /* Print the numbers. %a is C99 */
    printf("Double: %.20f (%a)\n", d, d);
    printf("Float: %.20f (%a)\n", f, f);
    printf("tmp: %.20f (%a)\n", tmp, tmp);

    if (superior) {
        /* If we wanted superior, and got a smaller value,
           get the next value */
        if (tmp < d)
            f = nextafterf(f, INFINITY);
    } else {
        if (tmp > d)
            f = nextafterf(f, -INFINITY);
    }
    printf("converted: %.20f (%a)\n", f, f);

    return 0;
}

在我的机器上,它打印:

Double: 0.10000000000000000555 (0x1.999999999999ap-4)
Float: 0.10000000149011611938 (0x1.99999ap-4)
tmp: 0.10000000149011611938 (0x1.99999ap-4)
converted: 0.09999999403953552246 (0x1.999998p-4)

这个想法是我将double值转换为一个float值——这可能小于或大于 double 值,具体取决于舍入模式。当转换回 时double,我们可以检查它是小于还是大于原始值。然后,如果 的值float不在正确的方向,我们float从转换后的数字中查看下一个数字在原始数字的方向上。

于 2010-01-07T03:07:04.273 回答
2

我在这里发布了执行此操作的代码:https ://stackoverflow.com/q/19644895/364818并将其复制到下面为您提供方便。

    // d is IEEE double, but double is not natively supported.
    static float ConvertDoubleToFloat(void* d)
    {
        unsigned long long x;
        float f; // assumed to be IEEE float
        unsigned long long sign ;
        unsigned long long exponent;
        unsigned long long mantissa;

        memcpy(&x,d,8);

        // IEEE binary64 format (unsupported)
        sign     = (x >> 63) & 1; // 1
        exponent = ((x >> 52) & 0x7FF); // 11
        mantissa = (x >> 0) & 0x000FFFFFFFFFFFFFULL; // 52
        exponent -= 1023;

        // IEEE binary32 format (supported)
        exponent += 127; // rebase
        exponent &= 0xFF;
        mantissa >>= (52-23); // left justify

        x = mantissa | (exponent << 23) | (sign << 31);
        memcpy(&f,&x,4);

        return f;
    }
于 2013-10-28T20:58:59.667 回答