|
|
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.