1

我已经开始说明何时需要将 IEEE-754 单精度和双精度数字转换为带有 base 的字符串10。有FXTRACT可用的指令,但它只提供以 2 为底的指数和尾数,因为数字计算公式为:

value = (-1)^sign * 1.(mantissa) * 2^(exponent-bias)

如果我有一些针对特定基数的对数指令,我将能够更改 2 个指数的基数 -表达式中的偏差部分,但目前我不知道该怎么做。我也在考虑使用标准舍入转换为整数,但它似乎无法使用,因为它不提供精确的转换。有谁知道这样做的方式/基本原则是什么?请帮忙。

我终于找到了另一个解决方案(它在 Java 中)

{
    /* handling -infinity, +infinity and NaN, returns "" if 'f' isn't one of mentioned */
    String ret = "";
    if ((ret = getSpecialFloats(f)).length() != 0)
        return ret;
}
int num = Float.toRawIntBits(f);
int exponent = (int)(((num >> 23) & 0xFF)-127); //8bits, bias 127
int mantissa = num & 0x7FFFFF; //23bits

/* stores decimal exponent */
int decimalExponent = 0;
/* temporary value used for calculations */
int sideMultiplicator = 1;
for (; exponent > 0; exponent--) {
    /* In this loop I'm calculating the value of exponent. MAX(unsigned int) = 2^32-1, while exponent can be 2^127 pr st like that */
    sideMultiplicator *= 2;
    /* because of this, if top two bits of sideMultiplicator are set, we're getting closer to overflow and we need to save some value into decimalExponent*/
    if ((sideMultiplicator >> 30) != 0) {
        decimalExponent += 3;
        sideMultiplicator /= 1000;
    }
}
for(; exponent < 0; exponent++) {
    /* this loop does exactly same thing as the loop before, but vice versa (for exponent < 0, like 2^-3 and so on) */
    if ((sideMultiplicator & 1) != 0) {
        sideMultiplicator *= 10;
        decimalExponent--;
    }
    sideMultiplicator /= 2;
}

/* we know that value of float is:
 *  1.mantissa * 2^exponent * (-1)^sign */
/* that's why we need to store 1 in betweenResult (another temorary value) */
int betweenResult = sideMultiplicator;
for (int fraction = 2, bit = 0; bit < 23; bit++, fraction *= 2) {
    /* this loop is the most important one: it turns binary mantissa to real value by dividing what we got in exponent */
    if (((mantissa >> (22-bit)) & 1) == 1) {
        /* if mantissa[bit] is set, we need to divide whole number by fraction (fraction is 2^(bit+1) ) */
        while (sideMultiplicator % fraction > 0 && (betweenResult >> 28) == 0) {
            /* as we needed it before: if number gets near to overflow, store something in decimalExponent*/
            betweenResult *= 10;
            sideMultiplicator *= 10;
            decimalExponent--;
        }
        betweenResult += sideMultiplicator/fraction;
    }
}

/* small normalization turning numbers like 15700 in betweenResult into 157e2 (storing zero padding in decimalExponent variable)*/
while (betweenResult % 10 == 0) {
    betweenResult /= 10;
    decimalExponent++;
}
/* this method gets string in reqested notation (scientific, multiplication by ten or just normal)*/
return getExponentedString(betweenResult, decimalExponent);
4

3 回答 3

7

格式化浮点数相当重要。搜索例如 Dragon4 算法(这里是一个结果)。

非常非常天真,你可以试试这个:

  1. 处理 NaN 和 Infinity。

  2. 打印标志(检查< 0)。假设今后这个数字是正实数。

  3. 如果>= 1,截断并使用熟悉的整数格式打印整数部分。(在任何具有浮点单元的硬件上都应该有一个机器指令。)

  4. 打印小数分隔符;现在继续乘以 10 并打印截断的整数。

  5. 当您达到所需的精度时停止;考虑正确舍入最后一位数字。

于 2013-07-13T17:02:37.370 回答
3

如果可以接受打印为 1.d1d2d3d4d5…*2^e1e2e3,则将浮点数转换为十进制 (-ish) 表示可能很简单。可以在此处找到实现。

如果您需要一个科学的 1.d1d2…*10^e1e2e3 表示,那么天真的方法就是反复除以 10 并从浮点格式的数字中提取数字。您将需要某种多精度整数库。(重复乘以 10 以提取该点之后的数字。)

于 2013-07-13T16:53:54.850 回答
1

Kerrek SB 的解决方案是正确的。但是你可以在没有任何循环(或更少的循环)的情况下更快地完成它。只需将小数部分乘以 10精度。如果您在浮点类型中进行数学运算,减少数字或乘法也会减少累积误差。要进行精确转换,您必须使用精度更高的浮点类型。

例如,您想将 0.1234567 转换为 5 位精度,将该数字乘以 10000 并获得 int 部分。如果需要四舍五入,将其乘以 100000 并四舍五入最后一个数字

于 2013-07-26T14:39:45.470 回答