【问题标题】:Different outputs for Java and CPPJava 和 CPP 的不同输出
【发布时间】:2021-10-01 17:25:00
【问题描述】:

我正在解决 Nth root of M 问题,我用 Java 解决了它,这是解决方案:

    public int NthRoot(int n, int m)
    {
        // code here
        int ans = -1;
        if (m == 0)
            return ans;
        if (n == 1 || n == 0)
            return m;
        if (m == 1)
            return 1;
        int low = 1;
        int high = m;
        while (low < high) {
            
            int mid = (low + high) / 2;
            int pow = (int)Math.pow(mid, n);
            if (pow == m) {
                
                ans = mid;
                break;
            }
            if (pow > m) {
                
                high = mid;
            } else {
                
                low = mid + 1;
            }
        }
        
        return ans;
    }

它通过了所有的测试用例。但是,当我使用 C++ 解决它时,一些测试用例没有通过。这是 C++ 解决方案:

    int NthRoot(int n, int m)
    {
        // Code here.
        int ans = -1;
        if (m == 0)
            return ans;
        if (n == 1 || n == 0)
            return m;
        if (m == 1)
            return 1;
        int low = 1;
        int high = m;
        while (low < high) {
            
            int mid = (low + high) / 2;
            int po = (int)pow(mid, n);
            if (po == m) {
                
                ans = (int)mid;
                break;
            }
            if (po > m) {
                
                high = mid;
            } else {
                
                low = mid + 1;
            }
        }
        
        return ans;
    } 

它没有通过的一个测试用例是:

6 4096
Java's output is 4 (Expected result)
C++'s output is -1 (Incorrect result)

当我用纸和笔追踪它时,我得到了与 Java 相同的解决方案。

但是,当我在 C++ 代码中使用 long long int 时,它工作得很好——但是在 Java 和 C++ 中 Int/int 的大小是一样的,对吧? (当我在 C++ 和 Java 中打印 INT_MAXInteger.MAX_VALUE 时,它输出相同的值。)

【问题讨论】:

  • 您是否尝试过调试 C++ 程序以查看何时何地出错?此外,如果没有正确的 minimal reproducible example(查看如何调用此函数以及使用哪些参数),无论如何都无法说出任何确定的内容。
  • "但是 Int 在 Java 和 C++ 中的大小是一样的,对吧?" - 在 Java 中,ints are 32-bit integrals。在 C++ 中,int 的大小在语言规范中没有确定。根据不同的因素,它至少可以是1632 位。如果需要 32 位有符号整数,请使用 int32_t
  • (int)pow(mid, n); -- 这并不能保证你会得到你想要的结果。 pow 函数是一个浮点函数,有pow() will not give the correct results 的实例。如果问题可以使用整数解决,请不要使用浮点函数、变量等。
  • 您是否检查过std::pow(2048, 6) 返回的内容以及您的数据类型(intlong long int)的最大值是多少,以及 c++ 与 java 相比如何处理溢出?
  • @Marivishnu 不,你不正确。再说一遍,Java 不是 C++,你正落入陷阱。 Java 具有固定的整数大小,C++ 整数大小取决于实现,但必须有相对于各种整数类型(short、int、long 等)的大小保证。

标签: java c++ algorithm binary-search


【解决方案1】:

正如您可能已经猜到的那样,问题是由于尝试将double 值转换为int 值,而该源大于int 的最大可表示值。更具体地说,它涉及到 Java 和 C++ 如何处理 while 循环开始附近的转换:int po = (int)pow(mid, n);

对于您的示例输入 (6 4096),pow 函数在第一次运行该循环时返回的值是 7.3787e+19,对于 int 值来说太大了。在 Java 中,当您尝试将太大的值转换为整数时,结果是整数类型可表示的最大值,如 this answer(我的粗体字)中指定的那样:

  • 该值一定是太大了(一个幅度很大的正值或者正无穷大),而且第一步的结果是最大的 int 或 long 类型的可表示值

但是,在 C++ 中,当源值超过 INT_MAX 时,行为未定义(根据此 C++11 Draft Standard):

7.10 浮点整数转换      [conv.fpint]

1 浮点的纯右值 type 可以转换为整数类型的纯右值。转换 截断;也就是说,小数部分被丢弃。行为是 如果截断的值不能在 目的地类型。

但是,尽管正式未定义,但许多/大多数编译器/平台会在发生这种情况时应用“翻转”,从而导致结果的 值非常大(通常为INT_MIN)。这就是 Visual Studio 中的 MSVC 所做的,给出 -2147483648 的值,从而导致 else 块运行……并且 保持 运行直到 while 循环达到其终止条件——此时除了初始的-1,点ans 永远不会被分配任何值。

您可以通过检查pow 调用返回的double 并在适当的情况下将po 设置为INT_MAX 来轻松解决问题,以模拟Java 行为:

    while (low < high) {
        int mid = (low + high) / 2;
        double fpo = pow(mid, n);
        int po = (int)(fpo);
        if (fpo > INT_MAX) po = INT_MAX; // Emulate Java for overflow
        if (po == m) {
            ans = (int)mid;
            break;
        }
        if (po > m) {
            high = mid;
        }
        else {
            low = mid + 1;
        }
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-03
    • 1970-01-01
    • 1970-01-01
    • 2013-10-27
    • 2018-06-03
    相关资源
    最近更新 更多