【问题标题】:Determine if an number (encoded as a string) will fit in a 64-bit integer in C++?确定一个数字(编码为字符串)是否适合 C++ 中的 64 位整数?
【发布时间】:2012-04-11 14:34:22
【问题描述】:

我正在寻找一种可移植的方法来 a) 将字符串转换为 64 位有符号整数 (int64_t),并且 b) 确定它是否不适合(溢出)。有没有办法做到这一点?

【问题讨论】:

  • strtoll 是一种可能性,但我不确定它是否足够便携。

标签: c++ string integer overflow portability


【解决方案1】:

strtoll 已经很便携了。如果不是您的情况,您可以随时使用 GNU C 运行时库并将其添加到您的项目中......

errno = 0;
long long val = strtoll (string, NULL, 0);
if (errno == ERANGE)
   // there was an overflow conversion error

【讨论】:

  • 但是 long int (这是 strtoll 返回的)不能保证是 64 位的,对吧?
  • @user793587:它返回long long int,如果编译器接受它,它几乎肯定是64位(或更多)。我想如果 long long 是 128 位值,可能会有问题。这可以通过一些运行时检查来解决....
  • strtoll 返回一个long long保证至少为 64 位。
  • @user793587:取决于您使用的编译器。 gcc 有。
【解决方案2】:

所以“长长”?带符号的 int64_ 可以容纳从 –9,223,372,036,854,775,808 到 9,223,372,036,854,775,807,您可以从字符串中看到。例如,使用 std::string:

int stringLength;
string myString("123456789");
stringLength = myString.length();

该代码获取您的字符串的长度。要确定它是否溢出,只需检查位数,如果可能溢出,检查第一个数字。要转换为 int64_,请使用强制转换:

http://www.learncpp.com/cpp-tutorial/44-type-conversion-and-casting/

该链接应该回答您的问题。 (但它适用于 C 风格的字符串。)最后一个澄清,你的字符串是 std::string 还是不是?

【讨论】:

  • 如果字符串是0x0000000000000000000000000000001怎么办?这不会溢出,但简单的长度检查会误导。
  • @wallyk,这就是你的字符串的样子?嗯...只是忽略前导零就可以了吗?
【解决方案3】:

一次一个地遍历字符串中的字符并生成整数。如果您正在解析的字符会导致溢出,那么您知道您即将溢出。这段代码是基本思想——不处理错误或负数,但应该给你这个想法......

bool ConvertToInt( const char* inString, int64_t& outInt )
{
    int64_t kInt64Max = 0x7fffffffffffffff;
    const char* c = inString;
    outInt = 0;
    while( *c != '\0' )
    {
          int charValue = *c - '0';
          //outInt will be assigned outInt * 10 + charValue, so to check if that will overflow
          //use algebra and move stuff around so that you can do the math without overflowing
          if( outInt > ( kInt64Max - charValue ) / 10 )
          {
               //overflow
               return false;
          }
          outInt = outInt * 10 + charValue;
          ++c;
    }
    return true;
}

如果您想在作业中获得满分,请务必处理负数和非数字字符。 [ 编辑增加 c ptr- 感谢您的提示 :) )

【讨论】:

  • +1:它当然是可移植的。 (它可以使用一些错误检查,比如字符串是否有数字。)
  • 我很失望标准库不能为我做便携,但这个解决方案似乎非常适合我的需求
  • 等等,你什么时候得到字符串中的下一个字符?您似乎将 c 初始化为指向第一个字符并且从不更改它
  • 另外,你在做 (kInt64Max - charValue) / 10 时是否需要担心截断?我现在想不出来
【解决方案4】:

为了迎合 Visual C++ 10.0(我写这个 11.0 处于测试阶段),它显然没有 strtoll 或任何等价物,

#include <assert.h>     // assert
#include <errno.h>      // errno
#include <stdint.h>     // int64_t
#include <string>       // std::string
#include <stdexcept>    // std::runtime_error, std::range_error
#include <stdlib.h>     // EXIT_FAILURE, EXIT_SUCCESS, strtoll
#include <iostream>
using namespace std;

#if defined( _MSC_VER )
#   if _MSC_VER <= 1600
#       include <ole2.h>
        inline long long strtoll( char const *str, char **str_end, int base )
        {
            assert(( "Only base 10 for Visual C++ 10 and earlier", base == 10 ));
            std::wstring const  ws( str, str + strlen( str ) );
            LONG64          result;
            HRESULT const   hr    = VarI8FromStr(
                ws.c_str(), 0, LOCALE_NOUSEROVERRIDE, &result
                );
            switch( hr )
            {
            case S_OK:
                if( str_end != 0 )
                {
                    *str_end = const_cast<char*>( str + strlen( str ) );
                }
                return result;
            case DISP_E_OVERFLOW:
                errno = ERANGE;
                if( str_end != 0 )
                {
                    *str_end = const_cast<char*>( str );
                }
                return (*str == '-'? LLONG_MIN : LLONG_MAX);
            default:
                errno = EILSEQ;
                if( str_end != 0 )
                {
                    *str_end = const_cast<char*>( str );
                }
                return 0;
            }
        }
#   endif
#endif

template< class Type >
bool hopefully( Type const& v ) { return !!v; }

bool throwX( string const& s )      { throw runtime_error( s ); }
bool throwRangeX( string const& s ) { throw range_error( s ); }

int64_t int64From( string const& s )
{
    errno = 0;

    int64_t const   result  = strtoll( s.c_str(), nullptr, 10 );

    if( errno == ERANGE )
        throwRangeX( "int64From: specificed nr too large" );
    else if( errno != 0 )
        throwX( "int64From: parsing failed" );
    return result;
}

int main( int argc, char** argv )
{
    try
    {
        int64_t const x   = int64From( argv[argc - 1] );
        wcout << x << endl;
        return EXIT_SUCCESS;
    }
    catch( runtime_error const& x )
    {
        cerr << "!" << x.what() << endl;
    }
    return EXIT_FAILURE;
}

然后对于 Visual C++ 10 及更早版本,使用 [oleaut32.lib] 链接。

我使用 MinGW g++ 和 Visual C++ 对此进行了测试。

PS:或者你也可以只写一个istringstream,但它不能可靠地告诉你为什么它失败了——而且它似乎是检测溢出的必要条件。 p>

【讨论】:

    【解决方案5】:

    根据 Joshua Glazer 的有用回复,我提出了以下解决方案,它可以进行错误检查,也适用于负整数:

    #define __STDC_LIMIT_MACROS
    #include <stdint.h>
    
    // convert a string to an integer, return whether successful
    bool string_to_int(string in, int64_t &out) {
      size_t pos = 0;
      if (in.size() == 0)
        return false;
      if (in[pos] == '+')
        pos++;
        out = 0;
      if (in[pos] == '-') {
        pos++;
        while (pos < in.size()) {
          if (in[pos] < '0' || in[pos] > '9')
            return false;
          int c = in[pos]-'0';
          if (out < (INT64_MIN+c)/10)
            return false;
          out = out*10-c;
          pos++;
        }
      } else {
        while (pos < in.size()) {
          if (in[pos] < '0' || in[pos] > '9')
            return false;
          int c = in[pos]-'0';
          if (out > (INT64_MAX-c)/10)
            return false;
          out = out*10+c;
          pos++;
        }
      }
      return true;
    }
    

    【讨论】:

      猜你喜欢
      • 2021-05-30
      • 1970-01-01
      • 2011-03-30
      • 1970-01-01
      • 2013-12-22
      • 1970-01-01
      • 1970-01-01
      • 2023-04-10
      • 2012-03-19
      相关资源
      最近更新 更多