|
|
nono 1.3.0
//
// nono
// Copyright (C) 2024 nono project
// Licensed under nono-license.txt
//
//
// MC68881 の Extended フォーマットを文字列にする
//
#include "exttostr.h"
#include "mystring.h"
#define BCD_N (3)
#define BCD_M (1'000'000'000)
// 5 の n 乗を返す。n は 0..13。
static uint32
pow5(int n)
{
static const uint32 table[] = {
1,
5,
25,
125,
625,
3125,
15625,
78125,
390625,
1953125,
9765625,
48828125,
244140625,
1220703125,
};
return table[n];
}
// 10進右シフト (というか 10 ずつの除算)。
// n には桁数ではなく 10のべき数のみ渡すこと。
static void
bcdshr(uint32 *digit, uint32 n)
{
uint64 t;
t = 0;
for (int i = BCD_N - 1; i >= 0; i--) {
t += (uint64)digit[i];
digit[i] = t / n;
t = (t % n) * (BCD_M);
}
}
// digit に v を乗算する。
// 戻り値は 10進で右シフトした桁数。
static uint32
bcdmul(uint32 *digit, uint32 v)
{
uint64 cy = 0;
for (int i = 0; i < BCD_N; i++) {
uint64 t = (uint64)digit[i] * v + cy;
digit[i] = (uint32)(t % BCD_M);
cy = t / BCD_M;
}
uint32 d = 0;
while (cy != 0) {
bcdshr(digit, 10);
digit[BCD_N - 1] += (cy % 10) * (BCD_M / 10);
cy /= 10;
d++;
}
return d;
}
// digit に v を加算する。
static uint32
bcdadd(uint32 *digit, uint32 v)
{
uint64 cy = v;
for (int i = 0; i < BCD_N; i++) {
uint64 t = (uint64)digit[i] + cy;
digit[i] = (uint32)(t % BCD_M);
cy = t / BCD_M;
}
return cy;
}
// MC68881 の Extended 形式を 10進数文字列に変換する。
std::string
ExtToStr(const uint32 *data)
{
std::string rv;
int s = data[0] >> 31;
int32 e = (int32)((data[0] >> 16) & 0x7fff);
uint64 m = ((uint64)data[1] << 32) | data[2];
if (e == 0x7fff) {
if (m != 0) {
if (m & (1ULL << 62)) {
return "QNAN";
} else {
return "SNAN";
}
} else {
if (s == 0) {
return "+INF";
} else {
return "-INF";
}
}
}
if (s == 0) {
rv = '+';
} else {
rv = '-';
}
if (e == 0 && (m & (1ULL << 63)) == 0) {
// denormal
e = -16383;
} else {
e -= 0x3fff;
}
// unnormal zero も 0 として返す。
if (m == 0) {
rv += '0';
return rv;
}
// BCD に変換。
uint32 a[BCD_N];
for (int i = 0; i < BCD_N; i++) {
a[i] = m % BCD_M;
m /= BCD_M;
}
e -= 63;
int d = 0;
uint32 x;
// 2進指数を 10進指数化する。
if (e > 0) {
// 2**e を 2**31 以下単位で掛けていく。
while (e > 0) {
x = e;
if (x >= 31) {
x = 31;
}
e -= x;
d += bcdmul(a, 1U << x);
}
} else {
// 2**abs(e) で割らないといけないが、10以外での除算は大変なので、
// m / 10**abs(e) * 5**abs(e) する。
d = e;
e = -e;
while (e > 0) {
x = e;
if (x >= 13) {
x = 13;
}
e -= x;
d += bcdmul(a, pow5(x));
}
}
// 上詰めにする。
while (a[BCD_N - 1] < BCD_M / 10) {
bcdmul(a, 10);
d--;
}
d += BCD_N * 9 - 1;
// 四捨五入。ここは 10進変換の丸めで、FPU の丸めモードとは関係ない。
#define BCD_ROUNDER (500'000)
bcdadd(a, BCD_ROUNDER);
// E形式、小数点以下20桁を返す。
rv += string_format("%u.%08u%09u%03u",
a[BCD_N - 1] / (BCD_M / 10),
a[BCD_N - 1] % (BCD_M / 10),
a[BCD_N - 2],
a[BCD_N - 3] / (BCD_ROUNDER * 2));
if (d != 0) {
rv += string_format("E%d", d);
}
return rv;
}
#if defined(TEST)
//
// How to use:
// % make test_exttostr
// % ./test_exttostr
//
#include <stdio.h>
int
main(int ac, char *av[])
{
#define E(M1,M2,M3) { 0x##M1, 0x##M2, 0x##M3 }
struct {
uint32 inp[3];
const char *exp;
} table[] = {
{ E(00000000, 00000000, 00000000), "+0" },
{ E(80000000, 00000000, 00000000), "-0" },
{ E(7fff0000, 00000000, 00000000), "+INF" },
{ E(ffff0000, 00000000, 00000000), "-INF" },
{ E(7fff0000, ffffffff, ffffffff), "QNAN" },
{ E(ffff0000, ffffffff, ffffffff), "QNAN" },
{ E(7fff0000, bfffffff, ffffffff), "SNAN" },
{ E(ffff0000, bfffffff, ffffffff), "SNAN" },
{ E(3fff0000, 80000000, 00000000), "+1.00000000000000000000" },
{ E(bfff0000, 80000000, 00000000), "-1.00000000000000000000" },
{ E(40050000, c8000000, 00000000), "+1.00000000000000000000E2" },
{ E(400e0000, ffff0000, 00000000), "+6.55350000000000000000E4" },
// 1 より大きい最小の区別可能な数
{ E(3fff0000, 80000000, 00000001), "+1.00000000000000000011" },
// From XEiJ/EFPBox.java
// 正規化数の最大値
{ E(7ffe0000, ffffffff, ffffffff), "+1.18973149535723176502E4932" },
// 正規化数の最小値
{ E(00000000, 80000000, 00000000), "+1.68105157155604675313E-4932" },
// 非正規化数の最大値
{ E(00000000, 7fffffff, ffffffff), "+1.68105157155604675295E-4932" },
// 非正規化数の最小値
{ E(00000000, 00000000, 00000001), "+1.82259976594123730126E-4951" },
};
int total = countof(table);
int failed = 0;
for (int i = 0; i < total; i++) {
const uint32 *inp = table[i].inp;
const char *exp = table[i].exp;
std::string act = ExtToStr(inp);
if (act != exp) {
printf("[%u] %08x_%08x_%08x expects \"%s\" but \"%s\"\n",
i, inp[0], inp[1], inp[2], exp, act.c_str());
failed++;
}
}
if (failed > 0) {
printf("%u tests, %u failed.\n", total, failed);
} else {
printf("%u tests, all passed.\n", total);
}
return 0;
}
#endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.