SRC質問掲示板Mk2
(現在 過去ログ106 を表示中)

HOME HELP 新規作成 新着記事 トピック表示 検索 掲示板新着情報RSS配信新着情報 過去ログ

[ 最新記事及び返信フォームをトピックトップへ ]

■19210 / inTopicNo.1)  16桁以上はご法度!?
  
□投稿者/ 匿名希望 -(2006/10/05(Thu) 10:19:42) [ID:2eMbhZ9o]
    2006/10/05(Thu) 14:38:30 編集(投稿者)

    時計関連のシステムの作成中に遭遇した怪現象です。

    計算で出される答えが16桁以上になる場合、正確に計算されません。

    具体的には16桁以上になると、
    先頭15桁はそのままで16桁目から全部0になります。
    切り捨ての時もあれば切り上げの時もあります。
    これは規則性があるのですが、私にはわかりませんでした。

    なんとかこの現象を回避しようとしているのですが、
    何をやってもうまくいきません。

    回避方法でいいですので、教えてください。

    バグであれば報告します。
引用返信/返信 削除キー/
■19212 / inTopicNo.2)  Re[1]: 16桁以上はご法度!?
□投稿者/ Unnamed -(2006/10/05(Thu) 22:19:44) [ID:RljYHlvN]
     SRCの数値計算は、 Double型(倍精度浮動小数点)という方式を
    取っています。そのため、数値計算はDouble型の扱える範囲に制限
    されます。結果として、これはバグではなく仕様ということになる
    と思います。
    
     具体的には、MSDNのVisual Basic 6.0リファレンスによると以下
    のとおりです。
    
    >倍精度浮動小数点数型の最大値は 1.79769313486232D + 308、
    >約 1.8 × 10 の 308 乗です。
    
     Double型の値が取り得る最大値は非常に大きいですが、その精度
    は最大約16桁と定められています。これを超える実数値は切り捨て
    られます。
    
     回避方法は、単純に余り大きな桁を使わないようにすることです。
    つまり、桁が大きくなったら別の変数に分割して計算するというこ
    とです。ただしこれは非常に面倒な処理なので、可能ならば最初か
    ら大きな桁になる数値を扱わないのが常道だと思います。
    
     以下は簡単な多倍長整数演算の例です。
    
    UPSplit:
        local src = Args(1)
        local f = RoundUp(Len(src) / 7, 0) - 1
        local lst i
        for i = 0 to f
            lst = "$(Mid(src, 7 * i + 1, 7)) $(lst)"
        next
    return lst
    
    
    UPPlus:
        local a = Args(1)
        local b = Args(2)
    
        if Len(a) + Len(b) <= 15 then
            return (a + b)
        endif
    
        a = UPSplit(a)
        b = UPSplit(b)
        local answer i temp shift n
        for i = 1 to Max(LLength(a), LLength(b))
            temp = LIndex(a, i) + LIndex(b, i) + shift
            n = Len(temp)
            if n > 7 then
                shift = Left(temp, n - 7)
                temp = Mid(temp, n - 7 + 1)
            else
                shift = 0
            endif
            answer = "$(temp)$(answer)"
        next
        if shift then
            answer = "$(shift)$(answer)"
        endif
    return answer
    
    
    プロローグ:
        a = 1234567999990
        b = 11
    
        talk
        $(UPPlus(a, b))
        end
    quit
    

引用返信/返信 削除キー/
■19216 / inTopicNo.3)  Re[2]: 16桁以上はご法度!?
□投稿者/ 匿名希望 -(2006/10/06(Fri) 07:48:36) [ID:2eMbhZ9o]
    2006/10/06(Fri) 07:49:02 編集(投稿者)

    提示された方法でうまくいきました。ありがとうございました。
解決済み!
引用返信/返信 削除キー/
■19218 / inTopicNo.4)  Re[3]: 16桁以上はご法度!?
□投稿者/ 匿名希望 -(2006/10/07(Sat) 16:36:31) [ID:2eMbhZ9o]
    誠に申し訳ないのですが、
    原理を教えていただけますか?

    カスタマイズが必要なのですが、
    カスタマイズができず困り果てております。

    解決済みの記事に追加投稿すみません。
引用返信/返信 削除キー/
■19235 / inTopicNo.5)  Re[4]: 16桁以上はご法度!?
□投稿者/ Unnamed -(2006/10/09(Mon) 00:13:23) [ID:RljYHlvN]
     大きな数値を計算する一般的な方法については「多倍長演算」
    「多倍長整数」などで検索してみてください。無限精度の小数は原
    理的に実現不可能なので出来ないと思った方が良いと思います。
    (代替としては固定小数点や有理数として扱う方法があります)
    
     ここで挙げたSRCコードについてということであれば、 以下にコ
    メントを付けたものを示しておきます。それからいくつかバグも見
    つかったので修正してあります。ただ、何分即席な実装ですので、
    これでもかなり処理を端折って(&インチキな手を使って)います。
    加算以外の処理はまた色々手を加える必要があるでしょう。また当
    然ながら、このコードを利用するにあたっては無制限かつ無保証で
    すので、その旨ご了承ください。
    
     一応、 さらに別の選択肢としてはSRC自体に多倍長整数のサポー
    トを求める方向性もありますが、あまり需要はなさそうですし、こ
    こで挙げたように非常に処理が複雑かつ今のSRCでは内部的に実装
    が難しそうなので、受け入れられる可能性は低いと思います。やは
    り大きな数値を扱わない(扱わずに済むように設計する)のが一番
    望ましいです。
    
    
    UPSplit:
        local src = Args(1)
        local slen = Args(2)
        local odd = slen mod 7
        local odds = Left(src, odd)
        src = Mid(src, odd + 1)
        local lst i
        for i = (slen \ 7 - 1) to 0 step -1
            lst = "$(lst) $(Mid(src, i * 7 + 1, 7))"
        next
    return "$(lst) $(odds)"
    
    
    # String UPPlus(String a, String b)
    #
    # 対応SRCバージョン: 2.2
    # 非限定精度の加算。引数は自然数(正の整数)に限る。
    # 16桁以上の値を表すには文字列として指定する必要が
    # あることに注意。
    #   UPPlus("123456789123456789", 12)
    #
    # ここで7桁ごとに計算している意味は特にない。
    # 加算ならば最大14桁ごとに計算してもよい。
    UPPlus:
        # 例文では以下の値が渡されたと仮定する
        # a = 12345678909876543
        # b = 1111111111
        local a = Args(1)
        local b = Args(2)
    
        # 桁数が少なければ普通に加算して返す
        local la = Len(a)
        local lb = Len(b)
        if la < 15 and lb < 15 then
            return (a + b)
        endif
    
        # 7桁ごとに区切ってリストにする
        # 下位桁の方がリスト前に来る
        # a = "9876543 4567890 123"
        # b = "1111111 111"
        a = UPSplit(a, la)
        b = UPSplit(b, lb)
    
        local answer i temp shift n
    
        # リストの要素ごとに加算
        # ループ回数は a および b のリスト長の長いほうにあわせる
        # Max(LLength(a), LLength(b)) = 3
        for i = 1 to Max(LLength(a), LLength(b))
    
            # a と b の1要素、および桁上がり分を足す
            # i = 1
            # temp = 9876543 + 1111111 + 0 = 10876543
            temp = LIndex(a, i) + LIndex(b, i) + shift
    
            # 計算後の桁数
            # n = 8
            n = Len(temp)
    
            # 桁数が8以上なら桁上がりとマークし、
            # tempを7桁にする
            # shift = 1
            # temp = 0876543
            if n > 7 then
                shift = 1
                temp = Right(temp, 7)
            else
                # 桁上がりが無いならshiftをリセット
                shift = 0
    
                # 7桁に満たないなら、
                # 桁がずれないように先頭を0で埋める
                if n < 7 then
                    temp = Format(temp, "0000000")
                endif
            endif
    
            # 計算結果の先頭に付加
            answer = "$(temp)$(answer)"
        next
    
        # 桁上がり分が残っていれば先頭に付加
        if shift then
            answer = "$(shift)$(answer)"
        endif
    
        # 先頭の連続した0を削除
        answer = RegExpReplace(answer, "^0*", "")
    
        # 空なら0に置き換える
        if answer = "" then
            answer = 0
        endif
    
        # answer = 12345680020987654
    return answer
    

引用返信/返信 削除キー/
■19243 / inTopicNo.6)  Re[5]: 16桁以上はご法度!?
□投稿者/ 匿名希望 -(2006/10/09(Mon) 19:03:48) [ID:2eMbhZ9o]
    使い方が理解できました。ありがとうございます。
解決済み!
引用返信/返信 削除キー/



トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

Pass/

HOME HELP 新規作成 新着記事 トピック表示 検索 掲示板新着情報RSS配信新着情報 過去ログ

- Child Tree -
- Antispam Version -