【问题标题】:Can I convert an int to string then count the number of digits?我可以将 int 转换为字符串然后计算位数吗?
【发布时间】:2015-10-13 00:29:44
【问题描述】:

我知道计算 int 位数的常用方法是使用循环,并一直除以 10 直到它消失。我很好奇能不能先把int转换成字符串,然后统计字符串的长度得到位数(假设只有正整数)?

如果这种方法可行,那么使用循环有什么区别?

【问题讨论】:

  • 您是在询问优化问题,但并未尝试进行任何分析来验证您的假设。您假设循环在某种程度上天生就很慢。您似乎忘记了编译器不会直接将您的 C++ 代码转换为机器代码。从中提取C++代码的含义,编译器生成具有相同含义的机器码。 这并不意味着生成的代码将执行与您在 C++ 中编写的代码相同的操作,只是表示效果相同。它的真正含义是什么?通常,循环会消失。个人资料,个人资料,个人资料。
  • 我一般会先把整个int转成string,因为这种方法更直接、易懂、可维护,而且我讨厌在int to string方法中硬编码“10”。
  • 您可能想看看 Andrei Alexandrescu 就优化提供的演示文稿中的these slides - 他使用和分析的一个示例是精确计算以 10 为底的数字中的数字。

标签: c++ stdstring


【解决方案1】:

是的,这种方法是可行的。
不同之处在于,如果先转换为字符串,则涉及一个额外的步骤。字符串转换实际上与您描述的循环类似。

这是一个如何通过字符串长度计算整数位数的示例:

#include <string>

int number_of_digits(int number) {
    // Using C++11
    return std::to_string(number).length();
}

【讨论】:

  • 既然多出了一个步骤,那么这个方法其实比使用循环慢吗?
  • 请注意 std::to_string() 是 C++11 中的新功能。对于旧版本,您可以改用std::ostringstreamstd::ostringstream oss; oss &lt;&lt; number; return oss.str().length();
【解决方案2】:

您可以使用itoa() 函数(在下面的示例中,_itoa_s()itoa()安全 版本)。此函数基本上将整数转换为字符串(itoa=整数到字母表)。

这是一个例子:

int number;
char temp[12];
cout << "Enter your number: ";
cin >> number;

//itoa(number, temp, 10);
//_itoa_s(number, temp, 12, 10);
_itoa_s(number, temp, 10);

cout << "The number of digits is: " << strlen(temp) << endl;

itoa() 采用 3 个参数:您要转换的数字 (number)、转换后将保存数字的缓冲区 (temp) 以及您要转换的数字系统转换为(10 用于 base-10,也就是十进制数字系统)。

_itoa_s() 采用第四个参数,即缓冲区的大小。还有一个模板版本的_itoa_s() 专门用于静态数组(在上面的示例中使用)。它需要 3 个参数,让编译器为第 4 个参数推断缓冲区大小。

另外,我为 temp 缓冲区选择了 12 个字符,因为整数的最大值是 2,147,483,647,它有 10 位数字,加上空终止符和一些额外对齐的空间。

【讨论】:

  • temp 不包含任何数据,当然也不是空终止的,当调用_itoa() 时,因此在第三个参数中使用strlen() 在这种情况下是错误的(更不用说这是_itoa_s() 输入的错误值),并且当_itoa_s() 尝试写入超出temp 的范围时,内存将被破坏。正确的代码应该更像这样:char *temp = new char[16]; _itoa_s(number, temp, 16, 10); cout &lt;&lt; ... &lt;&lt; strlen(temp) &lt;&lt; ...; delete[] temp;,或者干脆:char temp[16] = {0}; _itoa_s(number, temp, 10); cout &lt;&lt; ... &lt;&lt; strlen(temp) &lt;&lt; ...;
  • 感谢您的指正,我会更正我的代码,以免出现任何错误。
【解决方案3】:

对于输入 0,明显的除以 10 方法将返回 0,这可能是也可能不是您想要的。除此之外,两种方法都将返回相同的结果,但除以 10 要快得多

如果您像我在下面的示例中那样使用 std::string(它分配内存),那么除以 10 的方法很容易快数百倍。

int numlen_using_divide_by_10(int n) {
  int ret = 0;
  for (; n; n /= 10) ++ret;
  return ret;
}

int numlen_using_string(int n) {
  return std::to_string(n).size();
}

【讨论】:

    【解决方案4】:

    是的,你可以。不,您不想实际这样做。它将比使用循环慢 2 个数量级(~100 倍)。为什么?因为整数到字符串的转换不仅必​​须执行某种循环来生成字符串的数字,而且还必须分配 释放堆以保存字符串,除非字符串具有某种小字符串优化。

    在大多数情况下,人们会认为您的方法是一种丑陋的 hack。如果你想计算整数中的数字,你甚至不需要循环:

    constexpr int digits(int32_t x) {
      return 
        (x < 0 ? digits(-x) :
        (x < 10 ? 1 :   
        (x < 100 ? 2 :   
        (x < 1000 ? 3 :   
        (x < 10000 ? 4 :   
        (x < 100000 ? 5 :   
        (x < 1000000 ? 6 :   
        (x < 10000000 ? 7 :  
        (x < 100000000 ? 8 :  
        (x < 1000000000 ? 9 :  
        10))))))))));  
    }
    

    【讨论】:

    • 感谢您的解释。
    • GCC 可以做得更好:template&lt;class T&gt; static constexpr size_t digits() noexcept { return static_cast&lt;size_t&gt;( std::log10(std::numeric_limits&lt;T&gt;::max()) ) + 1; }
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-05
    • 1970-01-01
    • 2013-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多