【问题标题】:finding cube root in C++?在 C++ 中找到立方根?
【发布时间】:2011-05-15 05:17:40
【问题描述】:

当我试图找到一个数字的立方根时会发生奇怪的事情。

以下代码返回未定义。在 cmd 中:-1.#IND

cout<<pow(( double )(20.0*(-3.2) + 30.0),( double )1/3)

虽然这个工作非常好。在 cmd 中:4.93242414866094

cout<<pow(( double )(20.0*4.5 + 30.0),( double )1/3)

从数学的角度来看,它必须有效,因为我们可以从负数得到立方根。 Pow 来自 Visual C++ 2010 math.h 库。有什么想法吗?

【问题讨论】:

    标签: c++ math.h pow


    【解决方案1】:

    如果 x 为负数且 y 非整数,则来自&lt;cmath&gt;pow(x, y) 不起作用。

    这是 std::pow 的限制,如 C 标准和 cppreference 中所述:

    错误处理

    • 错误报告在 math_errhandling 中指定
    • 如果 base 是有限且负数,exp 是有限且非整数,则会发生域错误并且可能会发生范围错误。
    • 如果 base 为零且 exp 为零,则可能会发生域错误。
    • 如果 base 为零且 exp 为负,则可能会出现域错误或极点错误。

    有几种方法可以绕过这个限制:

    • Cube-rooting 和 1/3 次方是一样的,所以你可以做 std::pow(x, 1/3.)

    • 在 C++11 中,您可以使用 std::cbrt。 C++11 引入了平方根和立方根函数,但没有通用的 n 次根函数可以克服 std::pow 的限制。

    【讨论】:

    • 我不知道这一点。因为我即兴创作的 cmath 中没有第 n 个根函数。
    • 有趣的是,这个被接受的答案准确地解释了为什么“解决方法”不起作用!
    【解决方案2】:

    1/3 是一个特例。通常,负数的非整数幂是复数。 pow 检查整数根等特殊情况是不切实际的,此外,1/3 作为双精度数不完全是 1/3!

    我不了解可视化 C++ pow,但我的手册页显示错误:

    EDOM 参数x 为负数,y 不是整数值。这将产生一个复数。

    如果您想要负数的立方根,则必须使用更专业的立方根函数 - 或者偷工减料并取绝对值,然后取立方根,然后再乘以符号。

    请注意,根据上下文,1/3 幂的负数 x 不一定是您期望的负立方根。它可以很容易地成为第一个复杂的根,x^(1/3) * e^(pi*i/3)。这是mathematica 使用的约定;说它是未定义的也是合理的。

    【讨论】:

      【解决方案3】:

      虽然 (-1)^3 = -1,但您不能简单地取负数的有理幂并期望得到真正的响应。这是因为这个有理指数还有其他的解决方案,它们本质上是虚构的。
      http://www.wolframalpha.com/input/?i=x^(1/3),+x+from+-5+to+0

      同样,绘制 x^x。对于 x = -1/3,这应该有一个解决方案。但是,对于 x

      因此,不要指望 math.h 会变魔术使其效率低下,只需自己更改符号即可。

      【讨论】:

      • 嗯,它是一个必须有正负值的函数。我会为此添加一个 if。
      【解决方案4】:

      猜你得把负片拿出来,然后再放进去。如果你真的想的话,你可以让一个包装器为你做这件事。

      function yourPow(double x, double y)
      {
          if (x < 0)
              return -1.0 * pow(-1.0*x, y);
          else
              return pow(x, y);
      }
      

      【讨论】:

      • 这不是沿线 x
      • @DavidHeffernan,是的,确实如此,根据mathworld.wolfram.com/CubeRoot.html,我们有“但是,将立方根扩展到复平面会沿着负实轴进行分支切割,用于立方根”。
      【解决方案5】:

      不要使用 (double) 强制转换为 double,而是使用双精度数字常量:

      double thingToCubeRoot = -20.*3.2+30;
      cout<< thingToCubeRoot/fabs(thingToCubeRoot) * pow( fabs(thingToCubeRoot), 1./3. );
      

      应该做的伎俩!

      另外:不要在 C++ 项目中包含 &lt;math.h&gt;,而是使用 &lt;cmath&gt;

      或者,出于 buddhabrot 所述的原因,使用 &lt;complex&gt; 标头中的 pow

      【讨论】:

      • 呃,双重转换很好,这也失败了,因为它向 pow 发送了相同的值; @birryree 有答案
      【解决方案6】:

      pow( x, y )exp( y * log( x ) ) 相同(即等同于)

      如果 log(x) 无效,则 pow(x,y) 也是。

      同样地,你不能执行任何 0 的幂,尽管它在数学上应该是 0。

      【讨论】:

        【解决方案7】:

        C++11 具有 cbrt 函数(参见例如 http://en.cppreference.com/w/cpp/numeric/math/cbrt),因此您可以编写类似

        #include <iostream>
        #include <cmath>
        
        int main(int argc, char* argv[])
        {
           const double arg = 20.0*(-3.2) + 30.0;
           std::cout << cbrt(arg) << "\n";
           std::cout << cbrt(-arg) << "\n";
           return 0;
        }
        

        我无权访问 C++ 标准,所以我不知道如何处理否定论点...对 ideone http://ideone.com/bFlXYs 的测试似乎证实 C++ (gcc-4.8.1) 扩展了立方根这条规则cbrt(x)=-cbrt(-x)x&lt;0;对于这个扩展,你可以看到http://mathworld.wolfram.com/CubeRoot.html

        【讨论】:

          【解决方案8】:

          我在寻找 cubit root 并找到了这个线程,我突然想到以下代码可能有效:

          #include <cmath>
          using namespace std;
          
          function double nth-root(double x, double n){
              if (!(n%2) || x<0){
                  throw FAILEXCEPTION(); // even root from negative is fail
              }
          
              bool sign = (x >= 0);
          
              x = exp(log(abs(x))/n);
          
              return sign ? x : -x;
          }
          

          【讨论】:

          • 已经有一段时间了,但(sign==true)?return x:return -x;是认真的吗?这是有效的 C/++ 吗?为什么不选择return sign ? x : -x;x&gt;=0?sign=true:sign=false; 也一样 -> sign = (x &gt;= 0);
          • (sign==true)?return x:return -x; 是语法错误。没有人的建议是干净和正确的。
          • 这个线程的重点是算法而不是c++。我提供了一种有效的方法,任何人都应该明白我的意思。你也明白了吧?
          【解决方案9】:

          我认为您不应该将求幂与数字的第 n 根混淆。见好老Wikipedia

          【讨论】:

          • 在c++中没有rt(x,3)
          • @ilcredo 我的建议是数学建议
          【解决方案10】:

          因为 1/3 将始终返回 0,因为它将被视为整数... 试试 1.0/3.0... 这是我的想法,但尝试实施...... 并且不要忘记将包含 1.0 和 3.0 的变量声明为 double...

          【讨论】:

            【解决方案11】:

            这是我敲出来的一个小函数。

            #define uniform() (rand()/(1.0 + RAND_MAX))
            
            double CBRT(double Z)
            {
                double guess = Z;
                double x, dx;
                int loopbreaker;
            
            retry:
                x = guess * guess * guess;
                loopbreaker = 0;
                while (fabs(x - Z) > FLT_EPSILON)
                {
                    dx = 3 * guess*guess;
                    loopbreaker++;
                    if (fabs(dx) < DBL_EPSILON || loopbreaker > 53)
                    {
                        guess += uniform() * 2 - 1.0;
                        goto retry;
                    }
                    guess -= (x - Z) / dx;
                    x = guess*guess*guess;
                }
            
                return guess;
            }
            

            它使用 Newton-Raphson 求立方根。

            有时 Newton -Raphson 会卡住,如果根非常接近 0,那么导数可以 变大,它可以振荡。因此,如果发生这种情况,我已经夹紧并强制它重新启动。 如果您需要更高的准确性,您可以更改 FLT_EPSILON。

            【讨论】:

              【解决方案12】:

              如果您没有数学库,您可以使用这种方式计算三次根:

              立方根

              double curt(double x) {
                if (x == 0) {
                  // would otherwise return something like 4.257959840008151e-109
                  return 0;
                }
                double b = 1; // use any value except 0
                double last_b_1 = 0;
                double last_b_2 = 0;
                while (last_b_1 != b && last_b_2 != b) {
                  last_b_1 = b;
                  // use (2 * b + x / b / b) / 3 for small numbers, as suggested by  willywonka_dailyblah
                  b = (b + x / b / b) / 2;
                  last_b_2 = b;
                  // use (2 * b + x / b / b) / 3 for small numbers, as suggested by  willywonka_dailyblah
                  b = (b + x / b / b) / 2;
                }
                return b;
              }
              

              它源自下面的sqrt 算法。这个想法是bx / b / bx 的三次根开始越来越小。因此,两者的平均值更接近于x 的三次根。

              平方根和三次根(在 Python 中)

              def sqrt_2(a):
                  if a == 0:
                      return 0
                  b = 1
                  last_b = 0
                  while last_b != b:
                      last_b = b
                      b = (b + a / b) / 2
                  return b
              
              def curt_2(a):
                  if a == 0:
                      return 0
                  b = a
                  last_b_1 = 0;
                  last_b_2 = 0;
                  while (last_b_1 != b and last_b_2 != b):
                      last_b_1 = b;
                      b = (b + a / b / b) / 2;
                      last_b_2 = b;
                      b = (b + a / b / b) / 2;
                  return b
              

              与平方根相比,三次根中需要last_b_1last_b_2,因为b 闪烁。您可以修改这些算法来计算第四根、第五根等等。

              感谢我 11 年级的数学老师 Herr Brenner 告诉我sqrt 的算法。

              性能

              我在 16mhz 时钟频率的 Arduino 上对其进行了测试:

              【讨论】:

              • 您的curt 算法错误。应该是(2 * b + x / b / b) / 3
              • 这是一个很好的建议,因为它提高了小数字的运行时间。 x = 12 从 26 到 5 次迭代。但是,对于大数字:x = 7e+122 从 161 到 235 次迭代。 x = 7e+30 的迭代次数相等。谢谢。
              • 对于大量 X 尝试 (i) 将 1 / X 传递到例程并执行 1 / 结果; (ii) 除以 X 以下的已知立方数并乘以最后的立方根
              猜你喜欢
              • 2018-08-30
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-08-25
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-03-03
              相关资源
              最近更新 更多