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