2010-03-31 2 views
18

Я хочу проверить, является ли число double x целым числом в 10 раз. Возможно, я мог бы использовать cmath log10, а затем проверить, x == (int) x?C++: как я могу проверить, имеет ли число десять?

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

+2

@Yacoby Сила десяти - это номер формы '10^n', где' n' является целым числом, поэтому это, безусловно, будет работать. –

+5

Обратите внимание, что удвоения IEEE754 имеют только 52 бит точности. В результате 10^15 можно представить точно, но «double (10^16) == double (10^16 + 1)». В результате у вас будут либо ложные срабатывания, либо ложные негативы. Использование 'long long' (если доступно) может быть лучше. – MSalters

+0

Таким образом, 10E15 - это максимальная мощность 10, которая может быть представлена ​​точно. Ради любопытства, что является минимальным, 10Е-15? –

ответ

22

Таблица поиска будет на сегодняшний день самым быстрым и точным способом сделать это; только около 600 степеней 10 представлены в виде двойников. Вы можете использовать хеш-таблицу, или если таблица упорядочена от наименьшей до самой большой, вы можете быстро ее искать с помощью бинарной отбивной.

Это имеет то преимущество, что вы получите «удар» тогда и только тогда, когда ваш номер будет максимально приближенным IEEE до некоторой степени 10. Если это не то, что вы хотите, вам нужно быть более точным о том, как вы хотели бы, чтобы ваше решение справлялось с тем фактом, что многие полномочия 10 не могут быть точно представлены как двойные.

Лучший способ построения таблицы, вероятно, использовать преобразование строки -> float; так что, надеюсь, авторы вашей библиотеки уже решат проблему того, как сделать преобразование таким образом, чтобы дать наиболее точный ответ.

+3

Поскольку Helltone искал только точные соответствия, это было бы 16 возможных совпадений, а не 600. Тем более аргумент для проверки только этих констант. –

+1

@ Кристофер: На самом деле есть * 19 * точно представимые степени 10 в двойной точности. –

+2

@ Stephen Canon: Как вы получаете 19? Я делаю это 23, предполагая, что бинарный бит IEEE 754 удваивается. (10^0 до 10^22, все точно представляются, но 10^23 нет, падая ровно на полпути между двумя представимыми двойниками.) –

10

Ваше решение звучит неплохо, но я бы заменил точное сравнение с допуском.

double exponent = log10(value); 
double rounded = floor(exponent + 0.5); 
if (fabs(exponent - rounded) < some_tolerance) { 
    //Power of ten 
} 
+1

И, возможно, замените бросок на int вызовом 'floor', я нахожу, что выражает намерение лучше. –

+0

И до того, как кто-то пожалуется, это будет технически включать некоторые цифры, которые не обладают мощностью 10, я упомянул, что нотация с плавающей запятой IEE754 даже не имеет точных представлений для очень больших или очень малых мощностей 10. – smehmood

+0

Мне нужен точный ответ , не округлены. Если точный ответ невозможен, лучше предупредить пользователя. –

1

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

Поскольку количество чисел, которые являются 10^n (где n естественным), очень мало, , возможно, было бы быстрее просто использовать жестко скопированную таблицу поиска.

(Гадкий псевдо-код следующим образом :)

bool isPowerOfTen(int16 x) 
{ 
    if(x == 10  // n=1 
    || x == 100  // n=2 
    || x == 1000 // n=3 
    || x == 10000) // n=4 
    return true; 

    return false; 
} 

Это охватывает весь спектр int16 и если это все, что вам нужно может быть намного быстрее. (в зависимости от платформы.)

+2

В вопросе конкретно упоминается * удваивается *. –

+0

Выглядит хорошо для ints. –

+0

Это не было, когда я написал комментарий, но в этом случае вы, безусловно, правы. Но даже тогда его изменение на BCD и просто проверка нулевого нуля может быть быстрее. – Andreas

0

Как насчет кода, как это:


#include <stdio.h> 
#define MAX 20 
bool check_pow10(double num) 
{ 
    char arr[MAX]; 
    sprintf(arr,"%lf",num); 
    char* ptr = arr; 
    bool isFirstOne = true; 

    while (*ptr) 
    { 
    switch (*ptr++) 
    { 
     case '1': 
       if (isFirstOne) 
        isFirstOne = false; 
       else 
        return false; 
       break; 
     case '0': 
       break; 
     case '.': 
       break; 
     default: 
       return false; 
    } 
    } 

return true; 
} 

int main() 
{ 
    double number; 
    scanf("%lf",&number); 
    printf("isPower10: %s\n",check_pow10(number)?"yes":"no"); 
} 

Это не будет работать для отрицательных степеней 10, хотя.
EDIT: работает и для отрицательных полномочий.

+0

Креативное решение, но он использует удвоения. Удвои не могут представлять каждое число с полной точностью, поэтому вы иногда видите расчеты, которые выглядят так: (1/3) * 3 = 0.99999999 .... Рекомендованная таблица, предложенная выше, предпочтителен, поскольку она будет сохраните «самое близкое» представление мощности 10. Так что если 0.999999887723x10^24 является самым близким, вы получаете до 1.0000 * 10^25, вы можете сохранить значение «сразу немного» и по-прежнему получать положительный эффект. –

0

Если вам не нужно быть быстрым, используйте рекурсию. Псевдокод:

bool checkifpoweroften(double Candidadte) 
    if Candidate>=10 
     return (checkifpoweroften(Candidadte/10) 
    elsif Candidate<=0.1 
     return (checkifpoweroften(Candidadte*10) 
    elsif Candidate == 1 
     return 1 
    else 
     return 0 

Вы все еще должны выбирать между ложноположительных и ложноотрицательных и добавить допуски соответственно, как и другие ответы указали. Допуски должны применяться ко всем сравнениям, иначе, например, 9,99999999 не удалось бы провести сравнение> = 10.

+0

Это приведет к удалению стека, когда Candidadte будет очень большим или маленьким. –

+0

@nobugz: Вряд ли. Глубина рекурсии ограничена количеством бит в показателе «double»; обычно это повторяется не более 1023 раз (но не дает значимых результатов после 15 рекурсий) – MSalters

+2

Вы правы, временное ограничение кровоснабжения мозга. Надеюсь, что это временно ... –

2
bool power_of_ten(double x) { 
    if(x < 1.0 || x > 10E15) { 
     warning("IEEE754 doubles can only precisely represent powers " 
       "of ten between 1 and 10E15, answer will be approximate."); 
    } 
    double exponent; 
    // power of ten if log10 of absolute value has no fractional part 
    return !modf(log10(fabs(x)), &exponent); 
} 
+1

Предполагая, что 'log10' возвращает точное значение и не выключается целым ULP. –

5

Я боюсь, что вы находитесь в мире обид. Невозможно отличить очень большой или очень маленький номер с плавающей запятой до класса BigInt, потому что вы потеряли точность при использовании небольшого числа с плавающей запятой.

Например, float имеет только 6 цифр точности.Так что если вы представляете 10 как float, скорее всего, он будет преобразован как 1 000 000 145 или что-то в этом роде: ничто не гарантирует, что будут последние цифры, они не соответствуют точности.

Вы можете, конечно, использовать гораздо более точное представление, например double, которое имеет 15 цифр точности. Таким образом, вы должны иметь возможность представлять целые числа от 0 до 10 добросовестно.

Наконец, некоторые платформы могут иметь тип long long с все большей точностью.

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

Если вам действительно нужна эта точность, мое предложение не использовать число с плавающей запятой. Существуют математические библиотеки, доступные с реализациями BigInt, или вы можете сворачивать свои собственные (хотя эффективности трудно достичь).

0

как об этом:

bool isPow10(double number, double epsilon) 
{ 
    if (number > 0) 
    { 
     for (int i=1; i <16; i++) 
     { 
      if ((number >= (pow((double)10,i) - epsilon)) && 
       (number <= (pow((double)10,i) + epsilon))) 
      { 
       return true; 
      } 
     } 
    } 
    return false; 
} 

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

-1

Вариант this один:

double log10_value= log10(value); 
double integer_value; 
double fractional_value= modf(log10_value, &integer_value); 

return fractional_value==0.0; 

Обратите внимание, что сравнение 0.0 точно, а не в пределах конкретного эпсилон, так как вы хотите, чтобы убедиться, что log10_value целое.

EDIT: В связи с этим возникла разногласия из-за того, что log10 может быть неточным и общее понимание того, что вы не должны сравнивать двойники без эпсилона, а вот более точный способ определить, является ли двойное значение мощностью 10, используя только свойства степеней 10 и IEEE 754 удваиваются.

Во-первых, пояснение: двойной может представлять до 1E22, так как 1e22 имеет только 52 значащих бита. К счастью, 5^22 также имеет только 52 значащих бит, таким образом, мы можем определить, является ли двойник (2*5)^n для n= [0, 22]:

bool is_pow10(double value) 
{ 
    int exponent; 
    double mantissa= frexp(value, &exponent); 

    int exponent_adjustment= exponent/10; 

    int possible_10_exponent= (exponent - exponent_adjustment)/3; 

    if (possible_10_exponent>=0 && 
     possible_10_exponent<=22) 
    { 
     mantissa*= pow(2.0, exponent - possible_10_exponent); 

     return mantissa==pow(5.0, possible_10_exponent); 
    } 
    else 
    { 
     return false; 
    } 
} 

С 2^10==1024, что добавляет дополнительный бит значения, что мы должны извлечь из возможной мощности of 5.

+1

Никогда не сравнивайте double с == –

+0

Лучше бы: double double_epsilon = 1e-10; return fractional_value

+0

Ответ правильный, как указано. Если дробная часть имеет любое значение, отличное от 0, значение не является степенью 10. (Дробная часть log10 (10000000000.000099) составляет ~ 3.55e-15, например.) – 2010-03-31 20:47:15

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