Как я могу написать функцию C ++, возвращающую true, если действительное число точно представимо двойным? - PullRequest
5 голосов
/ 04 ноября 2008

Как я могу написать функцию C ++, возвращающую true, если действительное число точно представимо двойным?

bool isRepresentable( const char* realNumber )
{
   bool answer = false;
   // what goes here?
   return answer;
}

Простые тесты:

assert( true==isRepresentable( "0.5" ) );
assert( false==isRepresentable( "0.1" ) );

Ответы [ 6 ]

5 голосов
/ 04 ноября 2008

Разобрать число в форму a + N / (10 ^ k), где a и N - целые числа, а k - количество десятичных разрядов, которые у вас есть.

Пример: 12.0345 -> 12 + 345/10 ^ 4, a = 12, N = 345, k = 4

Теперь 10 ^ k = (2 * 5) ^ k = 2 ^ k * 5 ^ k

Вы можете представить свое число как точную двоичную дробь тогда и только тогда, когда вы избавитесь от члена 5 ^ k в знаменателе.

Результат будет проверен (N мод 5 ^ k) == 0

5 голосов
/ 04 ноября 2008

Святое домашнее задание, бэтмен! :)

Что делает это интересным, так это то, что вы не можете просто выполнить цикл (atof | strtod | sscanf) -> sprintf и проверить, вернули ли вы исходную строку. sprintf на многих платформах обнаруживает двойное «как можно ближе к 0,1» и печатает его, например, как 0,1, даже если 0,1 точно не представляется.

#include <stdio.h>

int main() {
    printf("%llx = %f\n",0.1,0.1);
}

печатает: 3fb999999999999a = 0,100000

в моей системе.

Реальный ответ, вероятно, потребовал бы анализа двойного числа, чтобы преобразовать его в точное дробное представление (0,1 = 1/10), а затем убедившись, что времена преобразования atof в знаменатель равны числителю.

Я думаю.

1 голос
/ 04 ноября 2008

Вот моя версия. sprintf преобразует 0,5 в 0,50000, нули в конце должны быть удалены.

РЕДАКТИРОВАТЬ: должен быть переписан для обработки чисел без десятичной точки, которые заканчиваются на 0 правильно (как 12300).

bool isRepresentable( const char* realNumber )
{
   bool answer = false;

   double dVar = atof(realNumber);
   char check[20];
   sprintf(check, "%f", dVar);

   // Remove zeros at end - TODO: Only do if decimal point in string
   for (int i = strlen(check) - 1; i >= 0; i--) {
     if (check[i] != '0') break;
     check[i] = 0;
   }

   answer =  (strcmp(realNumber, check) == 0);

   return answer;
}
0 голосов
/ 04 ноября 2008

Я бы преобразовал строку в ее числовое битовое представление (битовый массив или длинный), затем преобразовал бы строку в двойное число и посмотрел бы, совпадают ли они.

0 голосов
/ 04 ноября 2008

Это должно сработать:

bool isRepresentable(const char *realNumber)
{
    double value = strtod(realNumber, NULL);

    char test[20];
    sprintf(test, "%f", value);

    return strcmp(realNumber, test) == 0;
}

Вероятно, лучше всего использовать «безопасную» версию sprintf для предотвращения возможного переполнения буфера (возможно ли это даже в этом случае?)

0 голосов
/ 04 ноября 2008

Преобразование строки в число с плавающей запятой с большей областью, чем double. Примените это к двойному и посмотрите, совпадают ли они.

...