|
|
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
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.