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

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

unix.superglobalmegacorp.com

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