Annotation of nono/lib/exttostr.cpp, revision 1.1

1.1     ! root        1: //
        !             2: // nono
        !             3: // Copyright (C) 2024 nono project
        !             4: // Licensed under nono-license.txt
        !             5: //
        !             6: 
        !             7: //
        !             8: // MC68881 の Extended フォーマットを文字列にする
        !             9: //
        !            10: 
        !            11: #include "exttostr.h"
        !            12: #include "mystring.h"
        !            13: 
        !            14: #define BCD_N (3)
        !            15: #define BCD_M (1'000'000'000)
        !            16: 
        !            17: // 5 の n 乗を返す。n は 0..13。
        !            18: static uint32
        !            19: pow5(int n)
        !            20: {
        !            21:        static const uint32 table[] = {
        !            22:                1,
        !            23:                5,
        !            24:                25,
        !            25:                125,
        !            26:                625,
        !            27:                3125,
        !            28:                15625,
        !            29:                78125,
        !            30:                390625,
        !            31:                1953125,
        !            32:                9765625,
        !            33:                48828125,
        !            34:                244140625,
        !            35:                1220703125,
        !            36:        };
        !            37:        return table[n];
        !            38: }
        !            39: 
        !            40: // 10進右シフト (というか 10 ずつの除算)。
        !            41: // n には桁数ではなく 10のべき数のみ渡すこと。
        !            42: static void
        !            43: bcdshr(uint32 *digit, uint32 n)
        !            44: {
        !            45:        uint64 t;
        !            46:        t = 0;
        !            47:        for (int i = BCD_N - 1; i >= 0; i--) {
        !            48:                t += (uint64)digit[i];
        !            49:                digit[i] = t / n;
        !            50:                t = (t % n) * (BCD_M);
        !            51:        }
        !            52: }
        !            53: 
        !            54: // digit に v を乗算する。
        !            55: // 戻り値は 10進で右シフトした桁数。
        !            56: static uint32
        !            57: bcdmul(uint32 *digit, uint32 v)
        !            58: {
        !            59:        uint64 cy = 0;
        !            60:        uint64 t;
        !            61:        for (int i = 0; i < BCD_N; i++) {
        !            62:                t = (uint64)digit[i] * v + cy;
        !            63:                digit[i] = (uint32)(t % BCD_M);
        !            64:                cy = t / BCD_M;
        !            65:        }
        !            66: 
        !            67:        uint32 d = 0;
        !            68:        while (cy != 0) {
        !            69:                bcdshr(digit, 10);
        !            70:                digit[BCD_N - 1] += (cy % 10) * (BCD_M / 10);
        !            71:                cy /= 10;
        !            72:                d++;
        !            73:        }
        !            74: 
        !            75:        return d;
        !            76: }
        !            77: 
        !            78: // digit に v を加算する。
        !            79: static uint32
        !            80: bcdadd(uint32 *digit, uint32 v)
        !            81: {
        !            82:        uint64 cy = v;
        !            83:        uint64 t;
        !            84:        for (int i = 0; i < BCD_N; i++) {
        !            85:                t = (uint64)digit[i] + cy;
        !            86:                digit[i] = (uint32)(t % BCD_M);
        !            87:                cy = t / BCD_M;
        !            88:        }
        !            89:        return cy;
        !            90: }
        !            91: 
        !            92: // MC68881 の Extended 形式を 10進数文字列に変換する。
        !            93: std::string
        !            94: ExtToStr(const uint32 *data)
        !            95: {
        !            96:        std::string rv;
        !            97: 
        !            98:        int s = data[0] >> 31;
        !            99:        int32 e = (int32)((data[0] >> 16) & 0x7fff);
        !           100:        uint64 m = ((uint64)data[1] << 32) | data[2];
        !           101: 
        !           102:        if (e == 0x7fff) {
        !           103:                if (m != 0) {
        !           104:                        if (m & (1ULL << 62)) {
        !           105:                                return "QNAN";
        !           106:                        } else {
        !           107:                                return "SNAN";
        !           108:                        }
        !           109:                } else {
        !           110:                        if (s == 0) {
        !           111:                                return "+INF";
        !           112:                        } else {
        !           113:                                return "-INF";
        !           114:                        }
        !           115:                }
        !           116:        }
        !           117: 
        !           118:        if (s == 0) {
        !           119:                rv = '+';
        !           120:        } else {
        !           121:                rv = '-';
        !           122:        }
        !           123: 
        !           124:        if (e == 0 && (m & (1ULL << 63)) == 0) {
        !           125:                // denormal
        !           126:                e = -16383;
        !           127:        } else {
        !           128:                e -= 0x3fff;
        !           129:        }
        !           130: 
        !           131:        // unnormal zero も 0 として返す。
        !           132:        if (m == 0) {
        !           133:                rv += '0';
        !           134:                return rv;
        !           135:        }
        !           136: 
        !           137:        // BCD に変換。
        !           138:        uint32 a[BCD_N];
        !           139:        for (int i = 0; i < BCD_N; i++) {
        !           140:                a[i] = m % BCD_M;
        !           141:                m /= BCD_M;
        !           142:        }
        !           143:        e -= 63;
        !           144: 
        !           145:        int d = 0;
        !           146:        uint32 x;
        !           147: 
        !           148:        // 2進指数を 10進指数化する。
        !           149:        if (e > 0) {
        !           150:                // 2**e を 2**31 以下単位で掛けていく。
        !           151:                while (e > 0) {
        !           152:                        x = e;
        !           153:                        if (x >= 31) {
        !           154:                                x = 31;
        !           155:                        }
        !           156:                        e -= x;
        !           157:                        d += bcdmul(a, 1U << x);
        !           158:                }
        !           159:        } else {
        !           160:                // 2**abs(e) で割らないといけないが、10以外での除算は大変なので、
        !           161:                // m / 10**abs(e) * 5**abs(e) する。
        !           162:                d = e;
        !           163:                e = -e;
        !           164:                while (e > 0) {
        !           165:                        x = e;
        !           166:                        if (x >= 13) {
        !           167:                                x = 13;
        !           168:                        }
        !           169:                        e -= x;
        !           170:                        d += bcdmul(a, pow5(x));
        !           171:                }
        !           172:        }
        !           173: 
        !           174:        // 上詰めにする。
        !           175:        while (a[BCD_N - 1] < BCD_M / 10) {
        !           176:                bcdmul(a, 10);
        !           177:                d--;
        !           178:        }
        !           179:        d += BCD_N * 9 - 1;
        !           180: 
        !           181:        // 四捨五入。ここは 10進変換の丸めで、FPU の丸めモードとは関係ない。
        !           182: #define BCD_ROUNDER    (500'000)
        !           183:        bcdadd(a, BCD_ROUNDER);
        !           184: 
        !           185:        // E形式、小数点以下20桁を返す。
        !           186:        rv += string_format("%u.%08u%09u%03u",
        !           187:                a[BCD_N - 1] / (BCD_M / 10),
        !           188:                a[BCD_N - 1] % (BCD_M / 10),
        !           189:                a[BCD_N - 2],
        !           190:                a[BCD_N - 3] / (BCD_ROUNDER * 2));
        !           191: 
        !           192:        if (d != 0) {
        !           193:                rv += string_format("E%d", d);
        !           194:        }
        !           195:        return rv;
        !           196: }
        !           197: 
        !           198: #if defined(TEST)
        !           199: //
        !           200: // How to use:
        !           201: //  % make test_exttostr
        !           202: //  % ./test_exttostr
        !           203: //
        !           204: #include <stdio.h>
        !           205: 
        !           206: int
        !           207: main(int ac, char *av[])
        !           208: {
        !           209: #define E(M1,M2,M3) { 0x##M1, 0x##M2, 0x##M3 }
        !           210:        struct {
        !           211:                uint32 inp[3];
        !           212:                const char *exp;
        !           213:        } table[] = {
        !           214:                { E(00000000, 00000000, 00000000), "+0" },
        !           215:                { E(80000000, 00000000, 00000000), "-0" },
        !           216:                { E(7fff0000, 00000000, 00000000), "+INF" },
        !           217:                { E(ffff0000, 00000000, 00000000), "-INF" },
        !           218:                { E(7fff0000, ffffffff, ffffffff), "QNAN" },
        !           219:                { E(ffff0000, ffffffff, ffffffff), "QNAN" },
        !           220:                { E(7fff0000, bfffffff, ffffffff), "SNAN" },
        !           221:                { E(ffff0000, bfffffff, ffffffff), "SNAN" },
        !           222:                { E(3fff0000, 80000000, 00000000), "+1.00000000000000000000" },
        !           223:                { E(bfff0000, 80000000, 00000000), "-1.00000000000000000000" },
        !           224:                { E(40050000, c8000000, 00000000), "+1.00000000000000000000E2" },
        !           225:                { E(400e0000, ffff0000, 00000000), "+6.55350000000000000000E4" },
        !           226: 
        !           227:                // 1 より大きい最小の区別可能な数
        !           228:                { E(3fff0000, 80000000, 00000001), "+1.00000000000000000011" },
        !           229: 
        !           230:                // From XEiJ/EFPBox.java
        !           231:                // 正規化数の最大値
        !           232:                { E(7ffe0000, ffffffff, ffffffff), "+1.18973149535723176502E4932" },
        !           233:                // 正規化数の最小値
        !           234:                { E(00000000, 80000000, 00000000), "+1.68105157155604675313E-4932" },
        !           235:                // 非正規化数の最大値
        !           236:                { E(00000000, 7fffffff, ffffffff), "+1.68105157155604675295E-4932" },
        !           237:                // 非正規化数の最小値
        !           238:                { E(00000000, 00000000, 00000001), "+1.82259976594123730126E-4951" },
        !           239:        };
        !           240: 
        !           241:        int total = countof(table);
        !           242:        int failed = 0;
        !           243:        for (int i = 0; i < total; i++) {
        !           244:                const uint32 *inp = table[i].inp;
        !           245:                const char *exp = table[i].exp;
        !           246: 
        !           247:                std::string act = ExtToStr(inp);
        !           248:                if (act != exp) {
        !           249:                        printf("[%u] %08x_%08x_%08x expects \"%s\" but \"%s\"\n",
        !           250:                                i, inp[0], inp[1], inp[2], exp, act.c_str());
        !           251:                        failed++;
        !           252:                }
        !           253:        }
        !           254:        if (failed > 0) {
        !           255:                printf("%u tests, %u failed.\n", total, failed);
        !           256:        } else {
        !           257:                printf("%u tests, all passed.\n", total);
        !           258:        }
        !           259:        return 0;
        !           260: }
        !           261: #endif

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.