【问题标题】:C++: how can I test if a number is power of ten?C ++:如何测试一个数字是否是十的幂?
【发布时间】:2011-02-02 20:17:39
【问题描述】:

我想测试一个数字 double x 是否是 10 的整数幂。我也许可以使用 cmath 的 log10 然后测试 x == (int) x 是否?

编辑:实际上,我的解决方案不起作用,因为双精度数可以很大,比 int 大得多,也可以很小,比如分数。

【问题讨论】:

  • @Yacoby 十的幂是10^n 形式的数字,其中n 是一个整数,所以这肯定会起作用。
  • 请注意,IEEE754 双精度只有 52 位。因此,10^15 可以精确表示,但 double(10^16)==double(10^16+1)。结果,您将有误报或误报。使用long long(如果可用)可能会更好。
  • 所以 10E15 是可以精确表示的 10 的最大幂。出于好奇,10E-15 的最小值是多少?
  • @Helltone:10 的负数不能精确表示,因为 1/10 没有有限的二进制表示。

标签: c++ math floating-point


【解决方案1】:

查找表将是迄今为止最快、最精确的方法;只有大约 600 次方 10 可以表示为双打。您可以使用哈希表,或者如果该表从小到大排序,您可以使用二分法快速搜索。

这样做的好处是,当且仅当您的数字恰好是最接近 IEEE double 的 10 次幂时,您才会获得“命中”。如果这不是您想要的,您需要更精确地了解正是您希望您的解决方案如何处理许多 10 的幂不能完全表示为双精度数的事实。

最好的建表方式大概是使用string->float转换;这样,希望您的图书馆作者已经解决了如何以提供最精确答案的方式进行转换的问题。

【讨论】:

  • 由于 Helltone 只寻找完全匹配,这将是 16 个可能的匹配,而不是 600 个。更值得一提的是仅检查这些常量。
  • @Christopher:实际上,双精度中有 19 个 10 的可精确表示的幂。
  • @Stephen Canon:你是怎么得到 19 的?假设 IEEE 754 binary64 加倍,我将其设为 23。 (10^0 到 10^22 都是完全可表示的,但 10^23 不是,正好落在两个可表示的双精度之间。)
  • @Mark Dickinson:你当然是对的。我实际上并没有检查,大约一年前出于其他原因进行了计算。显然我记错了结果。 10^22 = 0x1.0f0cf064dd592p73 是二进制 64 格式中 10 的最大可精确表示的幂。
【解决方案2】:

您的解决方案听起来不错,但我会将精确比较替换为公差比较。

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

【讨论】:

  • 也许用floor的调用来替换转换为int,我发现这更能表达意图。
  • 在有人抱怨这将在技术上包括一些不是 10 的幂的数字之前,我会提到 IEE754 浮点表示法甚至没有非常大或非常小的 10 幂的精确表示.
  • 我想要一个精确的答案,而不是四舍五入。如果无法准确回答,最好警告用户。
  • @Helltone:如果你想要一个精确的答案,那么 some_tolerance 要么接近或等于零(后者不推荐,因为已告知原因)。只有整数数据类型才能为您的方案提供“准确”答案。
  • @smehmood:IEEE754 浮点表示法没有 10 的任何负幂的精确表示。0.1 是二进制的连续十进制(如果“十进制”是正确使用的词)。跨度>
【解决方案3】:

恐怕你正处于一个受伤的世界。无法将非常大或非常小的浮点数转换为 BigInt 类,因为在使用小浮点数时会丢失精度。

例如 float 只有 6 位精度。因此,如果您将 109 表示为 float 很可能它会被转换回 1 000 000 145 或类似的东西:没有什么能保证最后一位数字是什么,它们的精度不高。

您当然可以使用更精确的表示,例如 double,它具有 15 位精度。所以通常你应该能够忠实地表示从 0 到 1014 的整数。

最后,一些平台可能具有long long 类型,精度更高。

但无论如何,一旦您的值超过可转换回整数而不会丢失的位数...您无法测试它是否为 10 的幂。

如果你真的需要这种精度,我的建议是不要使用浮点数。 BigInt 实现中提供了数学库,或者您可以自己滚动(尽管很难实现效率)。

【讨论】:

    【解决方案4】:
    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);
    }
    

    【讨论】:

    • 当然,假设 log10 返回准确的值,并且不会被整个 ULP 关闭。
    • IEEE-754 实际上可以表示powers of 10 up to 10²²,尽管小于 2⁵³ 的 10 的最大幂是 10¹⁵
    【解决方案5】:

    根据平台,您的代码需要在日志上运行可能会非常昂贵。

    由于 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 范围,如果这就是您所需要的,可能会快很多。 (取决于平台。)

    【讨论】:

    • 问题中特别提到了doubles
    • 我写评论时没有,但在这种情况下你肯定是对的。但即便如此,将其更改为 BCD 并仅检查尾随零可能会更快。
    • 哦,仅仅因为不完整的伪代码而对明显正确的解决方案(查找表)投反对票对任何人都没有帮助;)
    • 1 是 10 的幂,即 10^0
    【解决方案6】:

    这样的代码怎么样:

    
    #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 的负幂。
    编辑: 也适用于负幂。

    【讨论】:

    • 一个创造性的解决方案,但他正在使用双打。双精度数不能完全准确地表示每个数字,这就是为什么您有时会看到如下所示的计算: (1 / 3) * 3 = 0.99999999.... 已经建议的查找表是首选,因为它将存储“最接近" 表示 10 的幂。因此,如果 0.999999887723x10^24 是最接近 1.0000*10^25 的值,则可以存储“稍微差一点”的值,仍然会得到正值。
    【解决方案7】:

    如果您不需要它很快,请使用递归。伪代码:

    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 比较。

    【讨论】:

    • 当候选人非常大或非常小时,这会破坏堆栈。
    • @nobugz:不太可能。递归深度受double的指数位数限制;通常这会递归不超过 1023 次(但在 15 次递归后无法产生有意义的结果)
    • 你说得对,大脑暂时的供血受限。希望是暂时的……
    【解决方案8】:

    怎么样:

    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;
    }
    

    我猜如果性能是一个问题,可以根据需要预先计算几个值,无论有没有 epsilon。

    【讨论】:

      【解决方案9】:

      this one 的变体:

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

      请注意,与 0.0 的比较是精确的,而不是在特定的 epsilon 内,因为您要确保 log10_value 是一个整数。

      编辑:由于log10 可能不精确以及一般理解不应该在没有 epsilon 的情况下比较双精度数,这引发了一些争议,这里有一种更精确的方法来确定双精度数是否是 10 的幂仅使用 10 次幂和 IEEE 754 双倍的属性。

      首先,澄清一下:double 最多可以表示 1E22,因为 1e22 只有 52 个有效位。幸运的是,5^22 也只有 52 个有效位,因此我们可以确定 n= [0, 22] 的双精度值是否为 (2*5)^n

      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,这增加了一点额外的意义,我们必须从可能的 5 幂中删除它。

      【讨论】:

      • 永远不要将 double 与 == 进行比较
      • 更好的是:double double_epsilon=1e-10;返回小数值
      • 给出的答案是正确的。如果小数部分有除 0 以外的任何值,则该值不是 10 的幂。(例如 log10(10000000000.000099) 的小数部分约为 3.55e-15。)
      • @Pavel,你读过帖子的最后一句话吗?与任何 epsilon 而不是 0.0 进行比较将给出不正确的结果。这是您需要与单个值而不是值范围进行比较的少数情况之一。
      • 如果该值可以表示为双精度数,那么 log10 必须返回精确的数学结果是在哪里写的?如果你能证明这一点,你就赢了(至少我的赞成票)。如果没有,那么这不能保证有效。
      猜你喜欢
      • 2010-09-11
      • 2010-10-10
      • 2017-01-09
      • 1970-01-01
      • 1970-01-01
      • 2013-01-06
      • 2017-03-21
      相关资源
      最近更新 更多