0

Я перешел к заявлению, когда мне нужно повернуть числа одиночной и двойной точности IEEE-754 в строки с базой 10. Существует FXTRACT инструкции доступно, но она обеспечивает только экспоненту и мантиссу для основания 2, так как формула расчета номера:Поверните поплавок в строку

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

Если бы я имел некоторые логарифмические инструкции для конкретных баз, я смог бы изменить базу- exponent - смещение часть в выражении, но в настоящее время я не знаю, что делать. Я также думал об использовании стандартного округленного преобразования в целое число, но он кажется непригодным, поскольку он не предлагает точное преобразование. Кто-нибудь знает, каков путь/основной принцип для этого? Пожалуйста помоги.

я наконец-то нашел другое решение (это в 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); 
+1

Почему бы не связать в стандартной библиотеке C и не вызвать 'printf' или' sprintf'? –

+0

дубликат http://stackoverflow.com/questions/22962040/easiest-way-to-convert-a-decimal-float-to-bit-representation-manual-based-on-i и http: // stackoverflow.com/questions/23540139/how-to-convert-floating-point-number-to-ieee-754-using-assembly –

+0

Это очень сложно сделать быстро, а @PascalCuoq уже написал [блог] (http: /www.exploringbinary.com/correct-decimal-to-floating-point-using-big-integers/) об этом –

ответ

3

Форматирование числа с плавающей запятой довольно нетривиально. Поиск, например. для алгоритма Dragon4 (here is one result).

Очень, очень наивности, вы можете попробовать это:

  1. Handle NaN и бесконечность.

  2. Распечатать знак (отметьте < 0). Предположим, что в дальнейшем число положительно вещественно.

  3. Если >= 1, обрезайте и используйте знакомое целочисленное форматирование для распечатки неотъемлемой части. (Для этого должна быть машинная инструкция для любого оборудования, имеющего блок с плавающей запятой.)

  4. Распечатать десятичный разделитель; теперь продолжайте умножать на 10 и печатайте усеченную интегральную цифру.

  5. Остановитесь, когда достигнете желаемой точности; подумайте о правильном округлении последней цифры.

+0

Большое спасибо. Кстати, что с этим «очень»? Если это будет «очень» медленно? – user35443

+0

@ user35443: Поскольку я взбесил это без всякой мысли об эффективности, я предполагаю, что это будет ужасно медленным. Я думаю, что момент Dragon4 должен был быть быстрым по сравнению с литературой в то время. Я не знаю никаких подробностей, но представьте, что моя идея делится на 10 * много *, и это довольно медленный расчет. –

1

Если это приемлемо для печати, как 1.d1d2d3d4d5 ... * 2^e1e2e3, то преобразование числа с плавающей запятой в десятичной (- ish) представление может быть простым. Реализация может быть найдена here.

Если вам нужно научное представление 1.d1d2 ... * 10^e1e2e3, тогда наивный подход к повторному делению на 10 и извлечению цифр из числа, которое у вас есть в формате с плавающей запятой. Вам понадобится какая-то многоцелевая целочисленная библиотека. (Повторно умножить на 10, чтобы извлечь цифры после точки.)

+0

Я не знаком с этим языком, но все равно спасибо за вашу помощь. Думаю, я выберу наивный путь. – user35443

0

Решение Kerrek SB верное. Но вы можете сделать это быстрее, без какого-либо цикла. Просто умножьте часть дроби с точностью 10 ^. Например, вы хотите конвертировать 0.1234567 с 5 знаками точности, умножить число на 10000 и получить int-часть. Если требуется округление, умножьте его на 100000 и округлите последнее число

+0

Такой подход является быстрым и легким в тех случаях, когда человек не заботится о точном округлении или о точности вблизи пределов типа. Я бы предположил, что последний шаг часто может быть упрощен путем умножения на 200000, преобразования в целое число, добавления одного и деления на два. Обратите внимание, что очень важно знать, что в разных сценариях числа могут различаться по-разному. У Turbo C 2.00 была ошибка, в которой 'printf ("% 1.2f ", 99.999999996);' определил бы, что слева от десятичной точки осталось две цифры, преобразуйте значение в строку ("100.00") и затем. .. – supercat

+0

... напечатать результат, начинающийся с двух цифр слева от десятичной точки (т.е. «00.00»). К сожалению. Тем не менее, ваш подход гарантирует, что даже если он не всегда даст точные значения, они вряд ли будут просто равными. – supercat

+0

Я наконец нашел другое решение, редактируя сообщение. – user35443

Смежные вопросы