Рассчитать количество знаков после запятой для значения с плавающей точкой БЕЗ библиотек? - PullRequest
2 голосов
/ 23 марта 2012

Мне нужно вычислить количество десятичных знаков для значения с плавающей запятой, например,

1234. 567 -> 3
2. 1233 ->4
4. 2432 -> 4

Моя первоначальная идея была:

number = 1234.567;  
...  
while (number - (int)number > 0.0)  
{  
  // Count decimal places  
  ...  
  number *= 10;  
}

Однако это вызывает проблемы с точностью с плавающей точкой в ​​условии while.Единственный безопасный обходной путь - это преобразование из числа с плавающей точкой в ​​строку, а затем подсчет знаков после запятой на основе строк.

Проблема заключается в следующем: я НЕ ДОЛЖЕН использовать никакие библиотеки, ни сторонние, ни стандартные библиотеки C ++экологические ограничения).Позже я знаю, как работать с символом *, но как я могу преобразовать значение с плавающей запятой в строку (т.е. символ *) без использования библиотек C ++?

Любая помощь очень ценится.


// Редактировать: это мой текущий подход, который все еще не работает (например, для 2.55555).Как выбрать правильный порог?

float abs(float number)  
{  
  return (number > 0.0 ? number : number * -1);  
}  

int round(float number)  
{  
  return (int)(number + 0.5);  
}  

void splitFloat(float* number, int* mantissa, int* exponent)  
{  
  while (abs(*number - round(*number)) > 0.00001)  
  {  
    // *number -= (int)*number; // ???  
    *number *= 10.0;  

    *mantissa = *number;  
    *exponent += 1;  

    cout << "Number: " << *number << ", Mantisse: " << *mantissa << ", Exponent: " << *exponent << endl;  
  }  
}

Ответы [ 3 ]

6 голосов
/ 23 марта 2012

Ваша первоначальная идея довольно близка, проблема в том, что с плавающей запятой выполняется округление, которое не дает точному результату.Вам нужно использовать порог вместо сравнения точно с 0,0, и вы должны разрешить, чтобы операция (int) могла усекаться неправильно, и вам следует вместо этого округлять.Вы можете округлить, добавив 0,5 перед усечением.

Вы также столкнетесь с проблемой, когда количество цифр больше не будет соответствовать int.Вы можете помочь, вычитая целую часть числа на каждом шаге.

Редактировать: Чтобы выбрать правильный порог, выберите максимальное количество десятичных знаков, которое вы хотите обработать.Если это 4, то наименьшее число, которое вы хотите вывести, равно 0.0001.Сделайте свой порог половиной этого или 0.00005.Теперь, каждый раз, когда вы умножаете свое число на 10, умножьте порог также на 10!

float threshold = 0.00005;
while (abs(*number - round(*number)) > threshold)  
{  
  *number *= 10.0;
  threshold *= 10.0;
  // ...
}

Если ваш float и int 32-битные, вам не нужно беспокоиться о вычитании int.Возврат мантиссы будет сложнее.

Кроме того, предупреждение, которое я хотел дать вам раньше, но забыл: это работает только для положительных чисел.

Еще одно предупреждениедиапазон значений для float довольно ограничен.Например, вы не сможете точно указать 1234.5670, и в конце вы получите постороннюю цифру.Изменение на double исправит это.

1 голос
/ 23 марта 2012

Ваша оригинальная идея хороша, но вы должны принять минимальную ошибку:

number = 1234.567;
...
while (fabs(number - round(number) > 0.00001)
{
    // Count decimal places
    ...
   number *= 10;
}

Обратите внимание, что когда вы говорите 1234.567, компьютер может сказать 1234.5670000001 или 1234.566999999, и вы не хотите считать все эти пакеты 0 или 9.

И остерегайтесь рондинга, а не усечения!

Но учтите, что он может работать не так, как ожидается, с отрицательными числами.

0 голосов
/ 23 марта 2012

Я думаю, вам лучше вычислить std::numeric_limits<float>::digits10 - log10(value), так как это максимальное количество десятичных цифр, которое будет иметь значение.Если у вас есть что-то для форматирования числа, вы можете отформатировать с такой точностью и убрать конечные нули.Если у вас нет чего-то для форматирования значения, вам, вероятно, придется сделать это много: подход явно нетривиален, если вы хотите хороших результатов.

...