【问题标题】:Turn float into string把浮点数变成字符串
【发布时间】:2013-07-13 16:41:04
【问题描述】:

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

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

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

我终于找到了另一个解决方案(在 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);

【问题讨论】:

标签: string assembly floating-point ieee-754 floating-point-precision


【解决方案1】:

格式化浮点数相当重要。搜索例如用于 Dragon4 算法 (here is one result)。

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

  1. 处理 NaN 和无穷大。

  2. 打印标志(检查&lt; 0)。假设以后这个数字是正实数。

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

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

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

【讨论】:

  • 非常感谢。顺便说一句,那个“非常”呢?如果这将是“非常”缓慢的方式?
  • @user35443:因为我在没有考虑效率的情况下就开始了这个,我想这会非常慢。我认为 Dragon4 的重点是与当时的库相比要快。我不知道任何细节,但想象一下我的想法除以 10很多,这是一个相当慢的计算。
【解决方案2】:

如果打印为 1.d1d2d3d4d5…*2^e1e2e3 是可以接受的,那么将浮点数转换为十进制 (-ish) 表示会很简单。可以在here 找到一个实现。

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

【讨论】:

  • 我不熟悉那种语言,但仍然感谢您的帮助。我想我会选择幼稚的方式。
【解决方案3】:

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

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

【讨论】:

  • 在不关心精确舍入或接近类型限制的精度的情况下,这种方法既快速又简单。我建议最后一步通常可以通过乘以 200000、转换为整数、加一并除以二来简化。请注意,注意数字在不同情况下可能会以不同方式取整,这一点非常重要。 Turbo C 2.00 有一个错误,printf("%1.2f",99.999999996); 会确定小数点左侧有两位数,将值转换为字符串(“100.00”),然后...
  • ...打印从小数点左侧两位数开始的结果(即“00.00”)。哎呀。不过,您的方法确保即使它不会总是产生精确的值,它们也不太可能是完全错误的。
  • 您仍然需要一个循环来将int( frac(x)*10000 ) 转换为字符串,一次一位十进制数字!但它将使用整数除法而不是 FP 乘法 (How do I print an integer in Assembly Level Programming without printf from the c library?)。 (除非您可以将 printf 用于整数格式但不能使用浮点数?但它必须在内部循环。)
  • 我猜一个 100 或 1000 个条目的查找表是合理的,可能在 C 字符串的打包缓冲区中具有 16 位索引。或者条目 是 C 字符串填充到固定长度。但是对于大多数用例来说,10^5 或更大的 LUT 可能并不合理。
  • 使用“5 位精度”,我不会想到“0.00000”,而是 0.0000012345”。数字 >=1 也是如此。对于“123456789”,我不会打印“12346”,但是“123460000”。您的回答似乎表明您在每种情况下都会做前者。
猜你喜欢
  • 2012-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-31
  • 1970-01-01
  • 2018-04-12
  • 1970-01-01
  • 2016-01-03
相关资源
最近更新 更多