strton(), ntostr()

strtol() の汎用版及びその逆の ntostr() を作ってみました。最初は strton.h なんてファイルで単体でアップしようかと思っていたのですが、ntostr() の為に汎用の整数部取得関数なんかも作成する羽目になりどう仕上げるべきか悩んでいるところです。悩んだまま寝かせてしまうのも勿体ないのでとりあえず以下に掲載しておきます。ちなみに現在の ntostr() の末尾の精度があまり面白い結果にならないので、そこをどうするかも思案中です。それからいちおう URR に対しても使用可能です。

#include <stddef.h>
#if defined(TRICKLIB_STRTON_WITH_EXCEPTION)
#include <stdexcept>
#else
#include <errno.h> // errno = ERANGE;
#endif

#include <stack>
#include <string>


//
//  整数部取得関数
//
template<class number_T> number_T get_integral_part(const number_T & value)
{
    //  変換コストが高い型を想定し、よく使う値を定数として用意しておく
    const number_T zero = static_cast<number_T>(0);
    const number_T one = static_cast<number_T>(1);
    const number_T two = static_cast<number_T>(2);
    const number_T minus = static_cast<number_T>(-1);
    
    //  戻り値
    number_T result = zero;
    
    std::stack<number_T> weight_stack;
    number_T prev_weight = zero;
    if (zero <= value)
    {
        //  正の値...
        
        //  対象の型で 2^0 から 2^n の値を計算
        for(number_T weight = one; weight <= value; weight *= two)
        {
            //  数値オーバーフローチェック
            if (weight <= prev_weight)
            {
                //  数値オーバーフローを起こした→これ以上大きな 2^x は扱えないのでループを終了
                break;
            }
            
            //  数値オーバーフローチェックの為に現在の 2^x を保存
            prev_weight = weight;
            
            //  2^x を保存
            weight_stack.push(weight);
        }
        
        //  value を超えない 2^0 から 2^n の値の組み合わせた値(== value の整数部)を作成
        while(false == weight_stack.empty())
        {
            number_T next_result = result +weight_stack.top();
            if (next_result <= value)
            {
                result = next_result;
            }
            weight_stack.pop();
        }
    }
    else
    {
        //  負の値...
        
        //  対象の型で -(2^0) から -(2^n) の値を計算
        for(number_T weight = minus; value <= weight; weight *= two)
        {
            //  数値オーバーフローチェック
            if (prev_weight <= weight)
            {
                //  数値オーバーフローを起こした→これ以上大きな -(2^x) は扱えないのでループを終了
                break;
            }
            
            //  数値オーバーフローチェックの為に現在の -(2^x) を保存
            prev_weight = weight;
            
            //  2^x を保存
            weight_stack.push(weight);
        }
        
        //  value を超えない -(2^0) から -(2^n) の値の組み合わせた値(== value の整数部)を作成
        while(false == weight_stack.empty())
        {
            number_T next_result = result +weight_stack.top();
            if (value <= next_result)
            {
                result = next_result;
            }
            weight_stack.pop();
        }
    }
    
    //  計算結果の返却
    return result;
}

//
//  小数部取得関数
//
template<class number_T> number_T get_decimal_part(const number_T & value)
{
    return value -get_integral_part(value);
}


template<class char_T>
int chrtoi(char_T c)
{
    if ('0' <= c && c <= '9') // '0' 〜 '9' が昇順連続した値となる文字エンコードであることに依存
    {
        return static_cast<int>(c -'0');
    }
    else
    if ('A' <= c && c <= 'Z') // 'A' 〜 'Z' が昇順連続した値となる文字エンコードであることに依存
    {
        return static_cast<int>(c -('A' -10));
    }
    else
    if ('a' <= c && c <= 'z') // 'a' 〜 'z' が昇順連続した値となる文字エンコードであることに依存
    {
        return static_cast<int>(c -('a' -10));
    }
    else
    {
        return -1;
    }
}

template<class number_T, class char_T>
number_T strton(const char_T * a_str, const char_T * * a_endptr = NULL, int a_base = 0)
{
    //  変換コストが高い型を想定し、よく使う値を定数として用意しておく
    const number_T zero = static_cast<number_T>(0);
    const number_T one = static_cast<number_T>(1);
    const number_T minus = static_cast<number_T>(-1);
    
    //  戻り値
    number_T result = zero;
    
    const char_T * i_body;
    const char_T * & i = a_endptr ? *a_endptr: i_body;
    i = a_str;
    
    //  基数指定の範囲チェック
    if (a_base < 0 || 1 == a_base || 36 < a_base)
    {
#if defined(TRICKLIB_STRTON_WITH_EXCEPTION)
        throw std::range_error("Range Error in strton()");
#else
        errno = ERANGE;
        return zero;
#endif
    }
    
    //  先頭に半角スペースがある場合はスキップ
    while(static_cast<char_T>(' ') == *i)
    {
        ++i;
    }
    
    //  符号の処理
    number_T sign_value = static_cast<number_T>(1);
    switch(*i)
    {
    case '+':
        ++i;
        break;
        
    case '-':
        ++i;
        sign_value = minus;
        if (one == sign_value)
        {
            return zero;
        }
        break;
    }
    
    if (zero == a_base)
    {
        switch(*i)
        {
        
        case '0':
            ++i;
            switch(*i)
            {
            
            //  2 進数
            case 'b':
            case 'B':
                ++i;
                a_base = 2;
                break;
                
            //  10 進数
            case 'd':
            case 'D':
                ++i;
                a_base = 10;
                break;
                
            //  16 進数
            case 'x':
            case 'X':
                ++i;
                a_base = 0x10;
                break;
                
            //  8 進数
            default:
                a_base = 010;
                break;
            
            }
            break;
            
        //  16 進数
        case 'a':
        case 'b':
        case 'c':
        case 'd':
        case 'e':
        case 'f':
        case 'A':
        case 'B':
        case 'C':
        case 'D':
        case 'E':
        case 'F':
            a_base = 0x10;
            break;
            
        //  10 進数
        default:
            a_base = 10;
            break;
            
        }
    }
    number_T base = static_cast<number_T>(a_base);
    
    while(*i)
    {
        int current = chrtoi<char_T>(*i);
        if ('.' == *i)
        {
            //  小数点以下の処理...
            
            number_T prev_weight = one;
            while(true)
            {
                number_T current_weight = prev_weight /base;
                if (zero == current_weight || prev_weight == current_weight)
                {
                    //  これ以上低い位を number_T では扱えない→変換を終了
                    break;
                }
                
                ++i;
                current = chrtoi<char_T>(*i);
                if (current < 0 || a_base <= current)
                {
                    //  無効な文字→変換を終了
                    break;
                }
                result += static_cast<number_T>(current) *current_weight;
                prev_weight = current_weight;
            }
            break;
        }
        if (current < 0 || a_base <= current)
        {
            //  無効な文字→変換を終了
            break;
        }
        number_T prev_result = result;
        result *= base;
        result += static_cast<number_T>(current);
        ++i;
        if (result <= prev_result)
        {
            if (one != minus && sign_value == minus && prev_result == minus *prev_result)
            {
                //  組み込み整数型などに見られる符号反転操作を行っても正の値にできないマイナスの最大値...
                
                current = chrtoi<char_T>(*i);
                if (current < 0 || a_base <= current)
                {
                    //  無効な文字→変換を終了
                    break;
                }
            }
            
#if defined(TRICKLIB_STRTON_WITH_EXCEPTION)
            throw std::range_error("Range Error in strton()");
#else
            errno = ERANGE;
            return zero;
#endif
        }
    }
    
    //  計算結果の返却
    return sign_value *result;
}

template<class char_T, class number_T>
std::basic_string<char_T> ntostr(number_T a_value, int a_base = 10)
{
    //  変換コストが高い型を想定し、よく使う値を定数として用意しておく
    const number_T zero = static_cast<number_T>(0);
    const number_T one = static_cast<number_T>(1);
    const number_T minus = static_cast<number_T>(-1);

    number_T value = a_value;
    //result = strtol(s, (char_T **)endptr, base);
    
    //  基数指定の範囲チェック
    if (a_base < 1 || 36 < a_base)
    {
#if defined(TRICKLIB_STRTON_WITH_EXCEPTION)
        throw std::range_error("Range Error in ntostr()");
#else
        errno = ERANGE;
        return "";
#endif
    }

    std::basic_string<char_T> result;
    number_T base = static_cast<number_T>(a_base);
    number_T major_value = get_integral_part(value);
    number_T minor_value = value -major_value;
    number_T magna_value = major_value;
    if (zero <= value)
    {
        //  正の値
    
        //  整数部の処理
        if (one <= major_value)
        {
            do
            {
                number_T next_value = get_integral_part(major_value /base);
                if (major_value <= next_value)
                {
                    return "-LARGENUM";
                }
                int current_number = static_cast<int>(major_value -(next_value *base));
                result = static_cast<char_T>("0123456789ABCDEFGHIJKLMNOPQRSTUVWYZ"[current_number]) +result;
                major_value = next_value;
            }
            while(one <= major_value);
        }
        else
        {
            result = static_cast<char_T>('0');
        }
        
        //  小数部の処理
        if (zero < minor_value)
        {
            result += static_cast<char_T>('.');
            number_T weight = one;
            do
            {
                number_T base_next_value = minor_value *base;
                number_T next_value = get_decimal_part(base_next_value);
                int current_number = static_cast<int>(base_next_value -next_value);
                result += static_cast<char_T>("0123456789ABCDEFGHIJKLMNOPQRSTUVWYZ"[current_number]);
                minor_value = next_value;
                number_T next_weight = weight /base;
                if (weight <= next_weight)
                {
                    break;
                }
                weight = next_weight;
                if (0 < current_number)
                {
                    number_T next_magna_value = magna_value +(weight *static_cast<number_T>(current_number));
                    if (next_magna_value <= magna_value)
                    {
                        break;
                    }
                    magna_value = next_magna_value;
                }
            }
            while(zero < minor_value);
        }
    }
    else
    {
        //  負の値
        
        //  整数部の処理
        if (major_value <= minus)
        {
            do
            {
                number_T next_value = get_integral_part(major_value /base);
                if (next_value <= major_value)
                {
                    return "-LARGENUM";
                }
                int current_number = static_cast<int>(major_value -(next_value *base));
                result = static_cast<char_T>("0123456789ABCDEFGHIJKLMNOPQRSTUVWYZ"[-current_number]) +result;
                major_value = next_value;
            }
            while(major_value <= minus);
        }
        else
        {
            result = static_cast<char_T>('0');
        }
        
        //  (マイナス)符号
        result = static_cast<char_T>('-') +result;
        
        //  小数部の処理
        if (minor_value < zero)
        {
            result += static_cast<char_T>('.');
            number_T weight = minus;
            
            do
            {
                number_T base_next_value = minor_value *base;
                number_T next_value = get_decimal_part(base_next_value);
                int current_number = static_cast<int>(base_next_value -next_value);
                result += static_cast<char_T>("0123456789ABCDEFGHIJKLMNOPQRSTUVWYZ"[-current_number]);
                minor_value = next_value;
                number_T next_weight = weight /base;
                if (next_weight <= weight)
                {
                    break;
                }
                weight = next_weight;
                if (current_number < 0)
                {
                    number_T next_magna_value = magna_value +(weight *static_cast<number_T>(-current_number));
                    if (magna_value <= next_magna_value)
                    {
                        break;
                    }
                    magna_value = next_magna_value;
                }
            }
            while(minor_value < zero);
        }
    }
    
    //  変換結果の返却
    return result;
}