challenge 16進数から10進数の変換

16進数を10進数に変換してください。

ただし、入出力は文字列とし、次の変換は最低必ずできなければいけないこととします。

  1. 0x12437308CCB6 →20080902065334

2.0x2C9C1227FC6520B →200904012311450123

あわせて、扱える最大の整数も明らかにしてください。

Posted feedbacks - Flatten

Nested Hidden

変換対象の16進数文字列はコマンドライン引数から。

use bigint で使用されるMath::BigIntには ”Arbitrary size integer" って書いてあるので、任意長の整数を扱えるのではないかと思いますが。

1
2
use bigint;
print "".hex shift;

文字列処理でごり押し。
速度の最適化はしていません。

理論上は無限桁の整数に対応していますが、メモリ・時間の誓約を加味すると……?
わたしのラップトップでは 150 桁まで確認できました。( *LOmake を使用 )
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
// ※負数は扱えない
// ※HSP3.2b2 のみ対応( strf, strtrim )

#module StrCalclator

#define true  1
#define false 0

#define ctype numrg(%1,%2,%3) \
    ( ((%2) <= (%1)) && ((%1) <= (%3)) )

//------------------------------------------------
// 数字 → 数値
//------------------------------------------------
#defcfunc charToNumber int chr
    if ( numrg(chr, '0', '9') ) {
        return chr - '0'
        
    } else : if ( numrg(chr, 'a', 'z') ) {
        return chr - 'a' + 10
        
    } else : if ( numrg(chr, 'A', 'Z') ) {
        return chr - 'A' + 10
    }
    return 0
    
//------------------------------------------------
// 数値 → 数字
//------------------------------------------------
#defcfunc numberToChar int n
    if ( numrg(n, 0, 9) ) {
        return n + '0'
    } else : if ( numrg(n, 10, 36) ) {
        return n + 'A'
    } else {
        return ' '
    }
    
//------------------------------------------------
// 桁を大きい方にそろえる
// 
// @ 足りない分は左側に0で詰めます
//------------------------------------------------
#deffunc Str_toSamePlaces var p1, var p2,  local len, local sResult
    len = strlen(p1), strlen(p2)
    
    if ( len(0) == len(1) ) {
        
    } else : if ( len(0) > len(1) ) {
        p2     = strf("%0"+ len(0) +"s", p2)
        len(1) = len(0)
    } else {
        p1     = strf("%0"+ len(1) +"s", p1)
        len(0) = len(1)
    }
    return len(0)
    
//------------------------------------------------
// 文字列同士の加法
//------------------------------------------------
#defcfunc StrCalc_add str p1, str p2, \
    local sLeft, local sRight, local sResult, \
    local cntRoundUp, local places, local n, local c
    
    sLeft      = p1
    sRight     = p2
    cntRoundUp = 0        // 繰り上がり (適当英単語)
    
    // 桁を大きい方にそろえる
    Str_toSamePlaces sLeft, sRight
    places = stat
    
    sdim sResult, places + 1
    
    // 桁ごとの足し算
    for i, places - 1, -1, -1    // 後ろの桁からの減数ループ
        
        // 桁の数値を得る
        c(0) = peek(sLeft,  i)
        c(1) = peek(sRight, i)
        n(0) = charToNumber(c(0))
        n(1) = charToNumber(c(1))
        
        // 繰り上がりを考慮して足し算する
        n(2)       = n(0) + n(1) + cntRoundUp
        cntRoundUp = 0
        
        // 繰り上がり?
        if ( n(2) >= 10 ) {
            cntRoundUp ++
            n(2)    -= 10
        }
        
        c(2) = numberToChar(n(2))
        
        // 結果に書き込む
        poke sResult, i, c(2)
        
    next
    
    // 最後の繰り上がり
    if ( cntRoundUp ) {
        sResult = strf("%c", numberToChar(cntRoundUp)) + sResult
    }
    
    return sResult
    
//------------------------------------------------
// 文字列同士の乗法
//------------------------------------------------
#defcfunc StrCalc_mul str p1, str p2,  \
    local sLeft, local sRight, local sMiddle, local sResult, \
    local cntRoundUp, local places, local n, local c
    
    sLeft      = p1
    sRight     = p2
    places     = strlen(sLeft), strlen(sRight)
    
    sdim sResult, places * 2
    sdim sMiddle, places + 1, places    // 途中式
    
    // 右辺の桁ごとにループ
    repeat places(1)
        c(1) = peek(sRight, places(1) - cnt - 1)
        n(1) = charToNumber(c(1))
        
        if ( n(1) == 0 ) {
            sMiddle(cnt) = "0"
            continue
        }
        
        cntRoundUp = 0        // 繰り上がり (適当英単語)
        
        // 桁ごとのかけ算
        for i, places(0) - 1, -1, -1            // 後ろの桁からの減数ループ
            
            // 桁の数値を得る
            c(0) = peek(sLeft,  i)
            n(0) = charToNumber(c(0))
            
            // 繰り上がりを考慮してかけ算する
            n(2)       = n(0) * n(1) + cntRoundUp
            cntRoundUp = 0
            repeat
                if ( n(2) >= 10 ) {
                    cntRoundUp ++
                    n(2)    -= 10
                } else {
                    break
                }
            loop
            
            c(2) = numberToChar(n(2))
            
            // 途中式に書き込む
            poke sMiddle(cnt), i, c(2)
            
        next
        
        // 最後の繰り上がり
        if ( cntRoundUp ) {
            sMiddle(cnt) = strf("%c%s", numberToChar(cntRoundUp), sMiddle(cnt))
        }
        
        // 後ろ側に 0 を並べて桁揃え
        sMiddle(cnt) += strf("%0"+ cnt +"s", "")
        
    loop
    
    // 途中式をすべて足し合わせる
    repeat places(1)
        sResult = StrCalc_add(sResult, sMiddle(cnt))
    loop
    
    return sResult
    
//------------------------------------------------
// 文字列の累乗
//------------------------------------------------
#defcfunc StrCalc_pow str p1, int p2,  local sResult
    sdim sResult
    sResult = "1"
    
    repeat p2
        sResult = StrCalc_mul(sResult, p1)
    loop
    return sResult
    
//------------------------------------------------
// 10進数以外の基数の文字列を10進数文字列に変換
// 
// @ 2 ~ 32 進数まで保証
// @ 負数は扱えない
//------------------------------------------------
#defcfunc StrCalc_toDigit str p1, int fromRadix, \
    local sFromRadix, local sInput, local sDigit
    if ( fromRadix <= 0 )  { return "" }        // エラー
    if ( fromRadix == 10 ) { return p1 }        // 変換する必要なし
    
    sdim sInput,  320
    sdim sDigit,  320
    sFromRadix = str(fromRadix)
    
    // 小文字にする
    sInput = p1
    places = strlen(sInput)
    
    // 十進数に直す
    repeat places
        c      = peek(sInput, places - (cnt + 1))
        stmp   = StrCalc_mul( str( charToNumber(c) ), StrCalc_pow(sFromRadix, cnt) )
        sDigit = StrCalc_add( sDigit, stmp )
    loop
    
    return sDigit
    
#global
    
*main
    sLeft   = "12437308CCB6",   "2C9C1227FC6520B",    "ff"
    sResult = "20080902065334", "200904012311450123", "255"
    
    repeat 3
        mes "#"+ cnt +"\n解答:"+ StrCalc_toDigit(sLeft(cnt), 16) +"\n正答:"+ sResult(cnt)
        mes
    loop
    
    stop
    
// おまけ
*LOmake
    n = 15                // 入力する16進数の桁数の10分の1
    sdim sHex, 1024
    repeat n
        sHex += "1234567890"
    loop
    
    sDig = StrCalc_toDigit(sHex, 16)
    logmes sDig
    stop

Squeak Smalltalk で。

Integer のクラスメソッド #readFrom:base: は、第一引数に変換したい文字列のストリームを、第二引数に何進数か指定してコールすることで十進数への変換が可能です。

桁数の制約は特にありません。

なお、文字列の先頭に16進数リテラル記述であることを示す '16r' が付けてある場合は、当該文字列にメッセージ asNumber を送るだけで済ませることもできます。

もちろんリテラルであれば、変換操作自体必要ありません。

1
2
3
4
5
6
7
8
Integer readFrom: '12437308CCB6' readStream base: 16      "=> 20080902065334 "
Integer readFrom: '2C9C1227FC6520B' readStream base: 16   "=> 200904012311450123 "

'16r12437308CCB6' asNumber      "=> 20080902065334 "
'16r2C9C1227FC6520B' asNumber   "=> 200904012311450123 "

16r12437308CCB6      "=> 20080902065334 "
16r2C9C1227FC6520B   "=> 200904012311450123 "

String#hex メソッドで変換ができます。 標準入力から入力された値を変換する場合には、以下のようにすればできます。

1
p ARGV.map{|s|s.hex}

任意長の整数を扱えるはずです.
1
main = interact $ unlines . map (show . (read :: String -> Integer)) . lines

.NETだとConvertクラスが異常に強力なのでこれでいけます。
取り扱える最大値について、int64.MaxValueから取得できる値が限界となります。
※ 当然これ以上に大きい値ではOverflowExceptionがでます
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
static void Main(string[] args)
{
    Console.WriteLine(ConvertTo16("12437308CCB6"));
    Console.WriteLine(ConvertTo16("2C9C1227FC6520B"));
    Console.WriteLine(ConvertTo16(Convert.ToString(Int64.MaxValue, 16)));
    Console.ReadLine();
}

static long ConvertTo16(string hexstr)
{
    return Convert.ToInt64(hexstr, 16);
}

//20080902065334
//200904012311450123
//9223372036854775807

ConvertTo16じゃなく、ConvertFrom16にすべきな挙句、.NETなのにotherにしてしまった…orz


Scheme にはそういう関数が用意されているのでそれを使っただけです。 使える値の大きさの上限は処理系によると思います。
1
2
3
(define (hex->dec h)
  (number->string
   (string->number h 16)))

変換機能は組み込みでありますが、内部的にはdouble型なのでdouble型のビット長(通常64bit)を超えると結果がおかしくなってしまいます。

gmpライブラリーを使うと任意の多倍長整数が扱えます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# builtin
> 0x12437308CCB6
[1] 20080902065334
> 0x2C9C1227FC6520B
[1] 200904012311450112
> typeof(0x2C9C1227FC6520B)
[1] "double"

# gmp
> library(gmp)
> as.bigz("0x12437308CCB6")
[1] "20080902065334"
> as.bigz("0x2C9C1227FC6520B")
[1] "200904012311450123"

Python 2.4

コマンドライン引数から16進数文字列を入力。

int関数で変換しています。

int型の範囲に収まらなければ自動的にlong型になり、long型には桁数の制限がないようです。

1
2
import sys
print int(sys.argv[1], 16)

PHP 5.1.6

hexdec()ではinteger型の範囲をこえる数値は、float型で返すみたいなので、
gmp_strval()を使いました。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// hexdec() ※うまくいかない
$ php -r 'echo hexdec($argv[1]),"\n";' 0x12437308CCB6
20080902065334
$ php -r 'echo hexdec($argv[1]),"\n";' 0x2C9C1227FC6520B
2.0090401231145E+17

// gmp_strval()
$ php -r 'echo  gmp_strval($argv[1]),"\n";' 0x12437308CCB6
20080902065334
$ php -r 'echo  gmp_strval($argv[1]),"\n";' 0x2C9C1227FC6520B
200904012311450123

F#で、入力HEX文字列は、コマンドライン引数で指定します。
uint64 の範囲で実行可
実行例:
>hex2dec 0x12437308ccb6
20080902065334

>hex2dec 0x2c9c1227fc6520b
200904012311450123
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#light

open System

[<STAThread>]
[<EntryPoint>]
let main(args) = 
    match args with 
    | [|hexString|] -> 
        printfn "%u" <| uint64 hexString
    | _ -> 
        printfn "Usage Hex2dec hexString"
    0

コマンドから実行。

Longの方がスマートですが、0x7fffffffffffffffまでの制限ありです。
BigIntegerは事実上無制限のようです。

BigDecimal -OKWave <http://74.125.153.132/search?q=cache:fEYO5fTGEsoJ:okwave.jp/qa715480.html+java+biginteger+%E6%9C%80%E5%A4%A7&cd=13&hl=ja&ct=clnk&gl=jp>
1
2
groovy -e "println Long.decode(args[0])" 0x12437308CCB6
groovy -e "println new BigInteger(args[0] - ~/0x/, 16)" 0x2C9C1227FC6520B

引数は、コマンドラインで指定します。
短い桁数の計算に分解しているので、特に制限はありません。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
use strict;
my $base = "0123456789abcdef";
my $num_16 = shift; $num_16 =~ s/^0x//; $num_16 = "\L$num_16";
my @num_16 = reverse split //, $num_16;
my @result;

foreach my $idx (0 .. $#num_16) {
  my $n = index($base, $num_16[$idx]);
  next unless $n;
  my @temp = calc($n, $idx);
  foreach my $idx (0 .. $#temp) {
    $result[$idx] += $temp[$idx];
    if ($result[$idx] > 9) {
      $result[$idx] -= 10; $result[$idx + 1]++;
    }
  }
}
print reverse(@result), "\n";

sub calc {
  my @work = reverse split //, shift(); my $x = shift;

  while ($x--) {
    $_ = $_ * 16 foreach @work;
    foreach my $idx (0 .. $#work) {
      if ($work[$idx] > 9) {
        my ($up, $n) = $work[$idx] =~ /^(.+)(.)$/;
        $work[$idx] = $n; $work[$idx + 1] += $up;
      }
    }
    if ($work[$#work] > 9) {
      my ($m, $n) = $work[$#work] =~ /^(.+)(.)$/;
      $work[$#work] = $n; push @work, reverse(split //, $m);
    }
  }
  return @work;
}

 BigIntegerで。BigIntegerは任意精度なので上限はありません。

1
2
3
4
5
6
7
import java.math.BigInteger;

public class ToDecimal {
    public static void main(String[] args) {
        System.out.println(new BigInteger(args[0].replaceFirst("^0x", ""), 16).toString());
    }
}

 BigIntを使って書いてみました。内部的にBigIntegerが使われているので特に上限はありません。

1
println(BigInt(args.first.replaceFirst("^0x", ""), 16).toString)

C99にはlong long型があるから簡単じゃん、と思ったら処理系の違いで苦労した。
strtoull で変換し、printf の %ull で出力するのが標準かと。
VC2008EE と gcc(MinGW) で確認しました。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>
#include <stdlib.h>

#ifdef _MSC_VER
  #define strtoull _strtoui64
#endif

#ifdef __MINGW32__
  #define FORMAT_LLU "%I64u"
#else
  #define FORMAT_LLU "%llu"
#endif

int main(void) {
    char buf[128];

    memset(buf, 0x00, sizeof(buf));
    if ( fgets(buf, sizeof(buf), stdin) ) {
        unsigned long long ll;

        ll = strtoull(buf, NULL, 16);

        printf(FORMAT_LLU, ll);
    }
    return 0;
}

書き忘れましたが、扱える整数の幅は符号なし64bitなので、 0 ~ 18446744073709551615 です。


1
2
let [|hex_string|] = Sys.argv;;
print_endline (Int64.to_string (Int64.of_string hex_string));;

F# で、Math.BigInt を使って桁の制限をなくしたバージョン
実行例:
> hex2dec "0x12437308CCB6";;
val it : Math.BigInt = 20080902065334I
> hex2dec "0x2C9C1227FC6520B";;
val it : Math.BigInt = 200904012311450123I
1
2
3
4
5
6
7
8
#light

let hex2dec (s:string) =
    let hs = s.[2..] //先頭の0x を取り除く
    let intToBigInt (x:int) = Math.BigInt(x)
    let bs = 16I
    let times_base x y = x * bs + y
    Seq.fold times_base 0I <| Seq.map (intToBigInt << int << (^) "0x" << string) hs

標準関数のparse-integerには基数を指定できますので、16進表記から変換することが可能です。
また、標準で#xというリーダーマクロが定義されていますので、これを用いて直接的に表記することも可能です。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
(parse-integer "12437308CCB6" :radix 16)
;=> 20080902065334
    12

(parse-integer "2C9C1227FC6520B" :radix 16)
;=> 200904012311450123
    15

(list #x12437308CCB6 #x2C9C1227FC6520B)
;=> (20080902065334 200904012311450123)

上限について書き忘れました。 Common Lispでは整数の大きさについて制限は設けられていません。 http://www.lispworks.com/documentation/HyperSpec/Body/t_intege.htm


   hex2dec '12437308CCB6'
20080902065334
   hex2dec '2C9C1227FC6520B'
200904012311450123
   hex2dec 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'
340282366920938463463374607431768211455
   (2x^128)-1
340282366920938463463374607431768211455
1
hex2dec =: 16x #. '0123456789ABCDEF' i. ]

初投稿です。 数値文字列として変換してみました。 変換後の最大値はおそらくNumber.MAX_VALUEになるかと。 # とりあえず4-500桁ぐらいまでは動作検証しました。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/* --------------------------------------------------------------------------- */
String.prototype.toCharArray=function(){
  var arr = new Array(this.length);
  for(var i = 0; i < this.length; i++){ arr[i] = this.charAt(i); };
  return arr;
};
/* --------------------------------------------------------------------------- */
function add(strnum1, strnum2){
  var arr1 = strnum1.toCharArray().reverse();
  var arr2 = strnum2.toCharArray().reverse();
  var max = (arr1.length > arr2.length ? arr1.length : arr2.length) - 1;
  var strsum = "";
  for(var i = 0, up = 0; i <= max; i++){
    var c1 = i < arr1.length ? parseInt(arr1[i],10) : 0;
    var c2 = i < arr2.length ? parseInt(arr2[i],10) : 0;
    c1 += up;
    c1 += c2;
    up = Math.floor(c1/10);
    c1 %= 10;
    strsum = c1 + strsum;
    if(i == max) strsum = up+strsum;
  }
  return strsum;
};
/* --------------------------------------------------------------------------- */
function sum(arr){
  var strsum = "";
  for(var i = 0; i < arr.length; i++){
    strsum = add(strsum, arr[i]);
  }
  return strsum.replace(/^0*/,"");
};
/* --------------------------------------------------------------------------- */
function multiple(a, b){
  var arr_a = a.toCharArray().reverse();
  var arr_b = b.toCharArray().reverse();
  var arr_c = new Array(arr_b.length);
  var max = arr_a.length-1;
  for(var i = 0, zeropad=""; i < arr_b.length; i++,zeropad+="0"){
    var str_c = "";
    for(var j = 0, up=0; j<arr_a.length; j++){
      var c = (parseInt(arr_a[j], 10) * parseInt(arr_b[i],10));
      c+=up;
      up = Math.floor(c/10);
      c %= 10;
      str_c = c+str_c;
      if(j==max && up>0) str_c=up+str_c;
    }
    str_c+=zeropad;
    arr_c[i] = str_c;
  }
  return sum(arr_c);
};
/* --------------------------------------------------------------------------- */
function hex2decimal(hexstr){
  var template = "0123456789ABCDEF";
  var arr = hexstr.toUpperCase().substring(2).toCharArray().reverse();
  for(var i = 0, hex="1"; i < arr.length; i++, hex=multiple(hex,"16")){
    var numc = "" + template.indexOf(arr[i]);
    var mult = multiple(numc, hex);
    arr[i] = mult;
  }
  var decstr=sum(arr);
  if(decstr.length==0) desctr="0";
  return decstr;
};

リンク貼るの失敗してますたorz

また、最大値について、「変換後の値がNumber.MAX_VALUE桁となる値」と修正します。


言語組み込みで扱える最大整数は64bitのlongです。なぜか標準ライブラリに16進文字列から整数に変換する関数がないのでC99からstrtollを借ります。

標準ライブラリに多倍長整数はあります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import core.stdc.stdlib: strtoll;
import std.bigint: BigInt;
import std.stdio;

void main() {
    // longは64bit
    long x = strtoll("0x12437308CCB6", null, 16);
    writeln(x); // 20080902065334
    
    // BigIntは多倍長整数
    BigInt y = "0x2C9C1227FC6520B";
    writeln(y); // 200904012311450123
}

unsigned long longは大抵の環境で64ビット符号無し整数として使えるだろうとあてにしています。<boost/cstdint.hpp>をインクルードして、boost::uint_least64_tあたりを使うのがおすすめですが、この程度のサンプルにBoostを用意させるのも大げさだろうということで、unsigned long longを使っています。
1
2
3
4
5
6
7
8
#include <iostream>

int main()
{
    unsigned long long n;
    std::cin >> std::hex >> n;
    std::cout << n << std::endl;
}

SQL Server 2008 で確認しました。 最大値は、bigint の限界なので、9,223,372,036,854,775,807 です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
WITH
  InputStrs(id, hex) AS (
    SELECT 1, '0x12437308CCB6'
    UNION ALL
    SELECT 2, '0x2C9C1227FC6520B'
  )
, HexStrs(id, hex) AS (
    SELECT
        id
      , SUBSTRING(hex, 3, LEN(hex))
    FROM
        InputStrs
  )
, MaxLen(max_len) AS (
    SELECT MAX(LEN(hex)) FROM HexStrs
  )
, Seq(id, n) AS (
    SELECT id, 1 FROM HexStrs
    UNION ALL
    SELECT
        Seq.id
      , n + 1
    FROM
        Seq INNER JOIN HexStrs ON Seq.id = HexStrs.id
    WHERE
        n + 1 <= (SELECT max_len FROM MaxLen)
  )
, Chs(id, ch, i) AS (
    SELECT
        id
      , SUBSTRING(hex, LEN(hex), 1)
      , 1
    FROM
        HexStrs
    UNION ALL
    SELECT
        Chs.id
      , SUBSTRING(hex, LEN(hex) - Chs.i, 1)
      , i + 1
    FROM
        Chs
          INNER JOIN HexStrs ON Chs.id = HexStrs.id
    WHERE
        i < (SELECT max_len FROM MaxLen)
  )
SELECT
    SUM(CASE
        WHEN ch = ''
          THEN 0
        WHEN ch LIKE '[A-F]'
          THEN ASCII(ch) - ASCII('A') + 10
          ELSE CAST(ch AS int)
        END * POWER(CAST(16 AS bigint), i - 1))
FROM
    Chs
GROUP BY
    id

Rubyでは数値リテラルとして16進整数表現があるので、そのまま文字列をevalすれば望む値が得られます。出力も文字列という出題なので、to_sで文字列にしています。

参考: リテラル - Rubyリファレンスマニュアル

ちなみにRuby自体には「最大の整数」という制限はなく、メモリの許す限り大きな整数を扱うことができます。

1
2
p eval("0x12437308CCB6").to_s    #=> "20080902065334"
p eval("0x2C9C1227FC6520B").to_s #=> "200904012311450123"

最大の整数は実装依存ですが、私の64bitマシンでは2^63-1が最大です。

1
2
3
4
5
6
7
8
9
h2i(){
  local -i i
  if [[ "$1" == 0x* ]]; then
    i="$1"
  else
    i=16#"$1"
  fi
  echo $i
}

有理数モードにすると桁数の制限がなくなるが
対応している演算は四則演算とべき乗だけである。
BVAL関数をそのまま使うと最初のサンプルは、うまくいくが
2番目のサンプルでは精度が足りない。
そこで、1桁ごとに分解して集計した。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
OPTION ARITHMETIC RATIONAL

FUNCTION hex2dec$(h$)
   LET n = LEN(h$)
   LET t = 0
   FOR i = 0 TO n - 1
      LET t = t + BVAL(h$(n - i : n - i),16) * 16 ^ i
   NEXT I
   LET hex2dec$ = STR$(t)
END FUNCTION

PRINT hex2dec$("12437308CCB6")      !=>20080902065334
PRINT hex2dec$("2C9C1227FC6520B")   !=>200904012311450123
END

苦肉の策です(笑) 全く、リテラルは2進数までサポートしているくせに…
1
2
3
4
5
6
import std.stdio;

void main() {
    long x = mixin("0x12437308CCB6");
    writeln(x); // 20080902065334
}

float型の場合は桁こぼれを起すという、出題のポイントを正しく理解されているようで、とても嬉しいです。

処理系の整数の大きさに制限がないことと、そのプログラムで扱える最大の整数、つまり正しく変換される上限が、同じだと、なぜ考えうるのかよく判りませんがLispって、そういうものなんですね。なるほど。


XSLT1.0で実装。
XPath1.0の数値型は浮動小数点数しか存在せず、その精度は処理系によって異なるようです。倍精度くらいで実装されているようで、この数値型をそのまま利用すると(29-51行)、条件2の変換が正確に行えませんでした。
そこで、入力された16進数を上位ビットと下位ビットに分けて数値化し、10進数として表示する時に2つの数を合成する手法を用いました(52-95行)。それでもFirefoxだと16桁でヘタってしまいましたが、Xalanは26桁まで頑張ってくれました。

実行結果(実行方法:適当なXMLにこのXSLTを適用)
 - Firefox 3.5.1:
0x12437308CCB6 = 20080902065334.
0x2C9C1227FC6520B = 200904012311450100.
0x12437308CCB6 = (bignum) 20080902065334.
0x2C9C1227FC6520B = (bignum) 200904012311450123.
0xCDEF89AB45670123 = (bignum) 14839230665905865403.
0x112233445566778899AABBCCDD = (bignum) 1357463230989497420518412782813.

 - Xalan 1.10.0:
0x12437308CCB6 = 20080902065334.
0x2C9C1227FC6520B = 200904012311450112.
0x12437308CCB6 = (bignum) 20080902065334.
0x2C9C1227FC6520B = (bignum) 200904012311450123.
0xCDEF89AB45670123 = (bignum) 14839230665905864995.
0x112233445566778899AABBCCDD = (bignum) 1357463230989497419223659171037.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="text" />
  <xsl:variable name="var" select="document('')/xsl:stylesheet/xsl:template[@name='var']" />
  <xsl:template match="/">
    <xsl:variable name="test1" select="'0x12437308CCB6'" />
    <xsl:variable name="test2" select="'0x2C9C1227FC6520B'" />
    <xsl:variable name="test3" select="'0xCDEF89AB45670123'" />
    <xsl:variable name="test4" select="'0x112233445566778899AABBCCDD'" />
    <xsl:call-template name="hex2dec">
      <xsl:with-param name="input" select="$test1" />
    </xsl:call-template>
    <xsl:call-template name="hex2dec">
      <xsl:with-param name="input" select="$test2" />
    </xsl:call-template>
    <xsl:call-template name="bighex2dec">
      <xsl:with-param name="input" select="$test1" />
    </xsl:call-template>
    <xsl:call-template name="bighex2dec">
      <xsl:with-param name="input" select="$test2" />
    </xsl:call-template>
    <xsl:call-template name="bighex2dec">
      <xsl:with-param name="input" select="$test3" />
    </xsl:call-template>
    <xsl:call-template name="bighex2dec">
      <xsl:with-param name="input" select="$test4" />
    </xsl:call-template>
  </xsl:template>
  <xsl:template name="hex2dec">
    <xsl:param name="input" />
    <xsl:value-of select="$input" /> = <!--
    --><xsl:apply-templates select="$var" mode="translate">
      <xsl:with-param name="input" select="substring($input,3)" />
    </xsl:apply-templates>.
<!--
  --></xsl:template>
  <xsl:template match="xsl:template" mode="translate">
    <xsl:param name="input"  />
    <xsl:param name="output" select="0" />
    <xsl:choose>
      <xsl:when test="$input=''">
        <xsl:value-of select="$output" />
      </xsl:when>
      <xsl:otherwise>
        <xsl:apply-templates select="." mode="translate">
          <xsl:with-param name="input" select="substring($input,2)" />
          <xsl:with-param name="output" select="$output * 16 + hex[@name=substring($input,1,1)]" />
        </xsl:apply-templates>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  <xsl:template name="bighex2dec">
    <xsl:param name="input" />
    <xsl:variable name="partition" select="floor((string-length($input)-2) div 2)" />
    <xsl:value-of select="$input" /> = (bignum) <!--
    --><xsl:apply-templates select="$var" mode="btranslate">
      <xsl:with-param name="input" select="substring($input,3,$partition)" />
      <xsl:with-param name="input2" select="substring($input,3+$partition)" />
    </xsl:apply-templates>.
<!--
  --></xsl:template>
  <xsl:template match="xsl:template" mode="btranslate">
    <xsl:param name="input" select="''" />
    <xsl:param name="input2" select="''" />
    <xsl:param name="output" select="0" />
    <xsl:param name="output2" select="0" />
    <xsl:variable name="in1" select="substring($input,1,1)" />
    <xsl:choose>
      <xsl:when test="$input=''">
        <xsl:choose>
          <xsl:when test="$input2=''">
            <xsl:variable name="outA" select="substring($output,1,string-length($output)-string-length($output2))" />
            <xsl:variable name="outB" select="substring($output,string-length($output)-string-length($output2)+1) + $output2" />
            <xsl:variable name="overflow" select="number(string-length($outB)>string-length($output2))" />
            <xsl:value-of select="$outA + $overflow" />
            <xsl:value-of select="substring($outB,1+$overflow)" />
          </xsl:when>
          <xsl:otherwise>
            <xsl:apply-templates select="." mode="btranslate">
              <xsl:with-param name="input2" select="substring($input2,2)" />
              <xsl:with-param name="output" select="$output*16" />
              <xsl:with-param name="output2" select="$output2 * 16 + hex[@name=substring($input2,1,1)]" />
            </xsl:apply-templates>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:when>
      <xsl:otherwise>
        <xsl:apply-templates select="." mode="btranslate">
          <xsl:with-param name="input" select="substring($input,2)" />
          <xsl:with-param name="input2" select="$input2" />
          <xsl:with-param name="output" select="$output * 16 + hex[@name=substring($input,1,1)]" />
        </xsl:apply-templates>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  <xsl:template match="text()" mode="translate" />
  <xsl:template match="text()" mode="btranslate" />
  <xsl:template match="text()" />
  <xsl:template name="var">
    <hex name="0">0</hex>
    <hex name="1">1</hex>
    <hex name="2">2</hex>
    <hex name="3">3</hex>
    <hex name="4">4</hex>
    <hex name="5">5</hex>
    <hex name="6">6</hex>
    <hex name="7">7</hex>
    <hex name="8">8</hex>
    <hex name="9">9</hex>
    <hex name="A">10</hex>
    <hex name="B">11</hex>
    <hex name="C">12</hex>
    <hex name="D">13</hex>
    <hex name="E">14</hex>
    <hex name="F">15</hex>
  </xsl:template>
</xsl:stylesheet>

arc> (hex2dec "12437308CCB6")
"20080902065334"
arc> (hex2dec "2C9C1227FC6520B")
"200904012311450123"

扱える整数については制限はありません。
1
(def hex2dec (x) (string (coerce x 'int 16)))

Index

Feed

Other

Link

Pathtraq

loading...