0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

IEEE 754 浮動小数点数 詳細表示 (binary 16/32/64/128)

Last updated at Posted at 2025-02-21

こちらは倍精度のみだったので様々な精度に対応したものを作ってみた。

  • binary16
    • シェーディング言語など
  • binary32
    • C/C++ の float 型
  • binary64
    • C/C++ の double 型
    • Python の float 型
    • JavaScript の Number 型
  • binary128
    • 普及するのは何時だろう?

拡張倍精度(80ビット)は非対応です。

See the Pen IEEE 754 浮動小数点数 詳細表示 by Ikiuo (@ikiuo) on CodePen.

sample.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>IEEE 754 浮動小数点数 詳細表示</title>
    <style>
     table {
         border: none;
         border-collapse: collapse;
         overflow-x: auto;
     }
     th, td {
         border: 1px solid #dfdfdf;
         padding: 2px 4px;
     }

     .left { text-align: left; }
     .center { text-align: center; }
     .right { text-align: right; }
     .vtop { vertical-align: top; }

     .large { font-size: large; }
     .small { font-size: small; }
     .xsmall { font-size: x-small; }
     .xxsmall { font-size: xx-small; }

     .ctext:hover {
         cursor: pointer;
         background-color: #e2e2e2;
     }
     .ftext { color: #006; }
     .htext { color: #600; }
     .ntext {
         word-break: break-all;
     }

     .eline { background: #edefef; }
     .oline { background: #f5f6f6; }

     .inpdata { background: #f5ffff; }
    </style>
  </head>
  <body>

    <script>

     const LOG10_2 = 0.3010299956639812;

     const gAbs = ((a, b) => a >= b ? a - b : b - a);

     function power5n(n) {
         var a = 1n;
         var b = 5n;
         while (n) {
             if (n & 1)
                 a *= b;
             b *= b;
             n >>= 1;
         }
         return a;
     }
     const power10n = (n => power5n(n) << BigInt(n));

     const BitLength = (x => x ? x.toString(2).length : 0);

     const toString16N = ((v, n) => {
         const l = Number(n);
         return ('0'.repeat(l) + v.toString(16)).slice(-l);
     });
     const toString16B = ((v, b) => toString16N(v, (b + 3n) >> 2n));

     /*
      * 分数表現型
      */
     class FractionBI {
         #S;

         nan;         // 非数のとき true
         finite;      // 有限値のとき true
         sign;        // 負のとき true
         numerator;   // 分子 (BigInt)
         denominator; // 分母 (BigInt)

         constructor(value) {
             const S = FractionBI;
             this.#S = S;

             switch (typeof value) {
                 case 'bigint':
                     this.#fromBigInt(value);
                     break;
                 case 'number':
                     this.#fromNumber(value);
                     break;
                 case 'string':
                     this.#fromString(value);
                     break;
                 default:
                     if (FractionBI.prototype.isPrototypeOf(value)) {
                         this.#fromRational(value);
                         break;
                     }
                     throw new Error("invalid type", {cause: value});
             }
         }

         // 非数を設定
         #setNaN(sign) {
             this.nan = true;
             this.finite = false;
             if (sign != null)
                 this.sign = sign;
             this.numerator = null;
             this.denominator = null;
         }

         // 無限大を設定
         #setInfinity(sign) {
             this.nan = false;
             this.finite = false;
             if (sign != null)
                 this.sign = sign;
             this.numerator = null;
             this.denominator = null;
         }

         // 有限値を設定
         #setFinite(sign, numerator, denominator) {
             if (!denominator)
                 throw new Error("invalid denominator", {cause: denominator});

             this.nan = false;
             this.finite = true;
             if (sign != null)
                 this.sign = sign;

             let fnum = numerator;
             let fden = denominator;
             if (fnum) {
                 while ((fnum % 5n) == 0n &&
                        (fden % 5n) == 0n) {
                     fnum /= 5n;
                     fden /= 5n;
                 }

                 const t = (fnum | fden);
                 const s = BigInt(BitLength(t & -t) - 1);
                 fnum >>= s;
                 fden >>= s;
             }
             this.numerator = fnum;
             this.denominator = fden;
         }

         // 0 を設定
         #setZero(sign) {
             this.nan = false;
             this.finite = true;
             if (sign != null)
                 this.sign = sign;
             this.numerator = 0n;
             this.denominator = 1n;
         }

         // FractionBI のコピー
         #fromRational(value) {
             this.nan = value.nan;
             this.finite = value.finite;
             this.sign = value.sign;
             this.numerator = value.numerator;
             this.denominator = value.denominator;
         }

         // BigInt 型を変換
         #fromBigInt(value) {
             this.nan = false;
             this.finite = true;
             this.sign = (value < 0n);
             this.numerator = (value < 0n) ? -value : value;
             this.denominator  = 1n;
         }

         // Number 型を変換
         #fromNumber(value) {
             const mb64 = (1n << 64n) - 1n;
             const mexp = ((1n << 11n) - 1n);
             const mman = ((1n << 52n) - 1n);

             const view = new DataView(new ArrayBuffer(8));
             view.setFloat64(0, value, true);

             const bin = mb64 & view.getBigInt64(0, true);
             const sign = (1n & (bin >> 63n));
             const exp = mexp & (bin >> 52n);
             const man = mman & bin;

             const fsign = sign != 0n;
             if (exp == 0n && man == 0n) {
                 this.#setFinite(fsign, 0n, 1n);
                 return;
             }
             if (exp == mexp) {
                 if (mman != 0)
                     this.#setNaN(fsign);
                 else
                     this.#setInfinity(fsign);
                 return;
             }

             const nnum = man | (exp ? (1n << 52n) : 0n);
             const nden = 1n << (exp ? 52n : 51n);
             const fnum = nnum << (exp > 1023n ? exp - 1023n : 0n);
             const fden = nden << (exp < 1023n ? 1023n - exp : 0n);
             this.#setFinite(fsign, fnum, fden);
         }

         // 文字列から変換
         #fromString(text) {
             const S = this.#S;

             text = text.trim().toLowerCase();

             let fsign = false;
             let fnum = 0n;
             let fden = 1n;

             let tpos = 0;
             if (tpos >= text.length) {
                 this.#setNaN(fsign);
                 return;
             }

             switch (text[tpos]) {
                 case '-':
                     fsign = true;
                     // FALLTHROUGH
                 case '+':
                     if (++tpos < text.length)
                         break;
                     this.#setNaN(fsign);
                     return;
             }

             switch (text.slice(tpos)) {
                 case 'nan':
                     this.#setNaN(fsign);
                     return;

                 case 'inf':
                 case 'infinity':
                     this.#setInfinity(fsign);
                     return;
             }

             const dtab = S.#base10Table;
             let btab = dtab;
             let base = 10n;

             if (tpos >= text.length) {
                 this.#setNaN(fsign);
                 return;
             }
             if (text[tpos] == '0') {
                 if (++tpos >= text.length) {
                     this.#setFinite(fsign, fnum, fden);
                     return;
                 }
                 switch (text[tpos]) {
                     case 'b':
                         btab = S.#base2Table;
                         base = 2n;
                         break;
                     case 'o':
                         btab = S.#base8Table;
                         base = 8n;
                         break;
                     case 'x':
                         btab = S.#base16Table;
                         base = 16n;
                         break;
                 }
                 if (base != 10n) {
                     if (++tpos >= text.length) {
                         this.#setNaN(fsign);
                         return;
                     }
                 }
             }

             let dot = false;
             for (;; tpos++) {
                 if (tpos >= text.length) {
                     this.#setFinite(fsign, fnum, fden);
                     return;
                 }

                 const c = text[tpos];
                 const n = btab[c];

                 if (n != null) {
                     fnum = fnum * base + n;
                     if (dot)
                         fden *= base;
                     continue;
                 }
                 if (c == '_' || c == ',')
                     continue;
                 if (c != '.')
                     break;
                 if (dot) {
                     this.#setNaN(fsign);
                     return;
                 }
                 dot = true;
             }

             let expm = base;
             switch (text[tpos]) {
                 case 'p':
                     if (expm == 10n) {
                         this.#setNaN(fsign);
                         return
                     }
                     expm = 2n;
                     // FALLTHROUGH
                 case 'e':
                     if (++tpos < text.length)
                         break;
                     // FALLTHROUGH
                 default:
                     this.#setNaN(fsign);
                     return;
             }

             let esign = false;
             switch (text[tpos]) {
                 case '-':
                     esign = true;
                     // FALLTHROUGH
                 case '+':
                     if (++tpos < text.length)
                         break;
                     this.#setNaN(fsign);
                     return;
             }

             let xnum = 0n;
             for (; tpos < text.length; tpos++) {
                 const c = text[tpos];
                 const n = dtab[c];
                 if (n == null) {
                     this.#setNaN(fsign);
                     return;
                 }
                 xnum = xnum * 10n + n;
             }
             if (xnum >= 10000000n) {
                 if (esign)
                     this.#setZero(fsign);
                 else
                     this.#setInfinity(fsign);
                 return;
             }

             let expv = 1n;
             while (xnum) {
                 if ((xnum & 1n))
                     expv *= expm;
                 expm *= expm;
                 xnum >>= 1n;
             }
             if (esign)
                 fden *= expv;
             else
                 fnum *= expv;

             this.#setFinite(fsign, fnum, fden);
         }

         static #base2Table = {
             '0': 0n, '1': 1n,
         }
         static #base8Table = {
             '0': 0n, '1': 1n, '2': 2n, '3': 3n,
             '4': 4n, '5': 5n, '6': 6n, '7': 7n,
         }
         static #base10Table = {
             '0': 0n, '1': 1n, '2': 2n, '3': 3n, '4': 4n,
             '5': 5n, '6': 6n, '7': 7n, '8': 8n, '9': 9n,
         }
         static #base16Table = {
             '0': 0n, '1': 1n, '2': 2n, '3': 3n,
             '4': 4n, '5': 5n, '6': 6n, '7': 7n,
             '8': 8n, '9': 9n, 'a': 10n, 'b': 11n,
             'c': 12n, 'd': 13n, 'e': 14n, 'f': 15n,
         }
     }

     /*
      * IEEE 754 要素
      */
     class IEEE754Parameter {

         //
         // 2進形式
         //

         static #makeBinary(exponentBits, mantissaBits) {
             const mantissaMask = (1n << mantissaBits) - 1n;
             const exponentMask = (1n << exponentBits) - 1n;
             const signPosition = exponentBits + mantissaBits;
             return {
                 mantissaBits,
                 mantissaMask,
                 mantissaPosition: 0n,
                 mantissaInt: 1n << mantissaBits,

                 exponentBits,
                 exponentMask,
                 exponentPosition: mantissaBits,
                 exponentBias: exponentMask >> 1n,

                 signBit: 1n << signPosition,
                 signPosition,

                 totalBits: signPosition + 1n,

                 getMantissa: (data => data & mantissaMask),
                 getExponent: (data => (data >> mantissaBits) & exponentMask),
                 getSign: (data => (data >> signPosition) & 1n),
             }
         }
         static #binaryCache = {
             16n: IEEE754Parameter.#makeBinary(5n, 10n),
             32n: IEEE754Parameter.#makeBinary(8n, 23n),
             64n: IEEE754Parameter.#makeBinary(11n, 52n),
             128n: IEEE754Parameter.#makeBinary(15n, 112n),
         }
         static getBinary(n) {
             const S = IEEE754Parameter;
             const bn = BigInt(n);
             const cache = S.#binaryCache
             const cached = cache[bn];
             if (cached != null)
                 return cached;
             if (bn % 32n)
                 return null;
             const nn = Number(n);
             const p = BigInt(nn - Math.round(Math.log2(nn) * 4) + 13);
             const param = S.#makeBinary(bn - p - 1n, p);
             cache[n] = param;
             return param;
         }

     }

     /*
      * IEEE 754 - 2進形式
      */
     class IEEE754Binary extends FractionBI {
         #S;
         #P;
         #round0;
         exponent;
         mantissa;

         get data() {
             const P = this.#P;
             return (this.sign ? P.signBit : 0n) |
                    (this.exponent << P.exponentPosition) |
                    this.mantissa;
         }

         get exponent2() {
             const dexp = this.exponent;
             return (dexp ? dexp : 1n) - this.#P.exponentBias;
         }

         get mantissaBits() { return this.#P.mantissaBits; }
         get exponentBits() { return this.#P.exponentBits; }
         get dataBits() { return this.#P.totalBits; }

         constructor(data_bits, value, round0) {
             super(value);
             this.#S = IEEE754Binary;
             this.#P = IEEE754Parameter.getBinary(data_bits);
             this.#round0 = Boolean(round0);
             this.#initialize();
         }

         #initialize() {
             const P = this.#P;

             if (!this.finite) {
                 this.exponent = P.exponentMask;
                 this.mantissa = this.nan ? P.mantissaInt >> 1n : 0n;
                 return;
             }

             let fnum = this.numerator;
             let fden = this.denominator;

             if (!fnum) {
                 this.exponent = 0n;
                 this.mantissa = 0n;
                 return;
             }

             const bnum = BitLength(fnum);
             const bden = BitLength(fden);
             let exp = BigInt(bnum - bden);

             if (exp >= 0)
                 fden <<= exp;
             else
                 fnum <<= -exp;
             if (fnum < fden) {
                 fnum <<= 1n;
                 exp -= 1n;
             }

             exp += P.exponentBias;
             if (exp <= 0n) {
                 fden <<= 1n - exp;
                 exp = 0n;
             }

             const xnum = fnum << P.mantissaBits;
             const xdiv = xnum / fden;
             const xrem = xnum % fden;

             const xcmp = (xrem << 1n) - fden;
             const xrnd = !this.#round0 && ((xcmp > 0n) || ((xcmp == 0n) && (xdiv & 1n))) ? 1n : 0n;

             let man = xdiv + xrnd;

             const mshift = exp > 0n ? 1n : 0n;
             if (man >= (P.mantissaInt << mshift)) {
                 man >>= mshift;
                 exp += 1n;
             }

             if (exp < P.exponentMask) {
                 this.exponent = exp;
                 this.mantissa = man & P.mantissaMask;
             } else {
                 this.finite = false;
                 this.exponent = P.exponentMask;
                 this.mantissa = 0n;
             }
         }

         #getMantissaEx() {
             return this.mantissa | (this.exponent ? this.#P.mantissaInt : 0n);
         }

         isZero() {
             return this.exponent == 0n && this.mantissa == 0n;
         }

         isPRN() {
             return this.finite && !this.isZero();
         }

         toHexData() {
             return '0x' + toString16B(this.data, this.dataBits);
         }

         #toStringSign(plus) {
             return this.sign ? '-': plus ? '+' : '';
         }

         #toStringNPRN(plus) {
             if (this.nan) return 'NaN';
             const vsign = this.#toStringSign(plus);
             if (!this.finite) return `${vsign}Infinity`;
             if (this.isZero()) return `${vsign}0`;
             return '';
         }

         static #toStringDN(dval, nexp) {
             const dstr = dval.toString();
             const mstr = '0'.repeat(Math.max(0, nexp - dstr.length)) + dstr;
             const mint = mstr.slice(0, mstr.length - nexp);
             const mdec = nexp ? mstr.slice(-nexp).replace(/0+$/, '') : '';
             const vint = mint.length ? mint : '0';
             const vdec = mdec.length ? `.${mdec}` : '';
             return vint + vdec;
         }

         static #s10ToExp(s) {
             const sign = (s[0] == '+' || s[0] == '-') ? s[0] : '';
             if (sign) s = s.slice(1);
             const dot = s.replace(/0+$/, '').split('.');
             const sint = dot[0];
             const sdec = dot[1] ?? '';
             if (sint == '0') {
                 const rdec = sdec.replace(/^0+/, '');
                 const tnum = `${sign}${rdec[0]}.${rdec.slice(1)}`;
                 const rnum = tnum.replace(/\.$/, '');
                 return `${rnum}e${rdec.length - sdec.length - 1}`;
             } else {
                 const rint = sint.replace(/^0+/, '');
                 const tnum = `${rint[0]}.${rint.slice(1)}${sdec}`;
                 const rnum = tnum.replace(/\.$/, '');
                 return `${rnum}e+${rint.length - 1}`
             }
         }

         toExponential(plus) {
             if (!this.isPRN())
                 return this.#toStringNPRN(plus);
             return this.#S.#s10ToExp(this.toString10(plus));
         }

         toString10(plus) {
             if (!this.isPRN())
                 return this.#toStringNPRN(plus);

             const S = this.#S;
             const bits = this.dataBits;
             const rnd0 = this.#round0;
             const toS = S.#toStringDN;

             const bexp = this.exponent2 - this.mantissaBits;
             const pexp = bexp > 0n ? bexp : 0n;
             const nexp = bexp < 0n ? Number(-bexp) : 0;
             const dp5n = power5n(nexp);
             const dval = (this.#getMantissaEx() << pexp) * dp5n;

             const dcol = Math.trunc(LOG10_2 * (BitLength(dp5n << pexp) - 1));
             let dmod = power10n(dcol);
             let dcan = [dval];
             for (;;) {
                 const fcan = dcan.map(v => v - v % dmod);
                 const ccan = fcan.map(v => [v, v + dmod]).flat();
                 const ncan = ccan.filter(v => {
                     const c = new S(bits, toS(v, nexp), rnd0);
                     return (c.finite &&
                             c.exponent == this.exponent &&
                             c.mantissa == this.mantissa);
                 });
                 if (!ncan.length)
                     break;
                 dcan = ncan;
                 dmod *= 10n;
             }
             dcan.sort((a, b) => {
                 const xa = gAbs(a, dval);
                 const xb = gAbs(b, dval);
                 return xa == xb ? 0 : xa < xb ? -1 : 1;
             });
             return this.#toStringSign(plus) + this.#S.#toStringDN(dcan[0], nexp);
         }

         toString10F(plus) {
             if (!this.isPRN())
                 return this.#toStringNPRN(plus);
             const bexp = this.exponent2 - this.mantissaBits;
             const pexp = bexp > 0n ? bexp : 0n;
             const nexp = bexp < 0n ? Number(-bexp) : 0;
             const dval = (this.#getMantissaEx() << pexp) * power5n(nexp);
             return this.#toStringSign(plus) + this.#S.#toStringDN(dval, nexp);
         }

         toString16(plus) {
             if (!this.isPRN())
                 return this.#toStringNPRN(plus);
             const mbsz = this.mantissaBits;
             const mshl = -mbsz & 3n;
             const hdec = toString16B(this.mantissa << mshl, mbsz + mshl);
             const hman = `0x${this.exponent ? 1 : 0}.${hdec}`;
             const exp2 = this.exponent2;
             const hexp = (exp2 >= 0 ? '+' : '') + `${exp2}`;
             return this.#toStringSign(plus) + `${hman}p${hexp}`;
         }

         toSepSignExpMan() {
             const sign = this.sign ? 1 : 0;
             const exp = toString16B(this.exponent, this.exponentBits);
             const man = toString16B(this.mantissa, this.mantissaBits);
             return `${sign}:${exp}:${man}`
         }
     }

     class IEEE754Binary16 extends IEEE754Binary { constructor() { super(16, ...arguments); } }
     class IEEE754Binary32 extends IEEE754Binary { constructor() { super(32, ...arguments); } }
     class IEEE754Binary64 extends IEEE754Binary { constructor() { super(64, ...arguments); } }
     class IEEE754Binary128 extends IEEE754Binary { constructor() { super(128, ...arguments); } }

     /*
      *
      */

     const viewOption = {}
     const binaryName = {
         16: '半精度',
         32: '単精度',
         64: '倍精度',
         128: '四倍精度',
     }
     const binaryList = Object.keys(binaryName).map(v => Number(v));
     const viewList = { bin: binaryList }

     const valueName = {
         'dat': '記憶域',
         'sem': '負:指:仮',
         'hex': '16進',
         'exp': '指数',
         'dec': '非指数',
         'xct': '厳密値',
     }
     const valueNameKeys = Object.keys(valueName);

     const optionName = {
         'rnd0': 'ゼロ方向丸め',
         'flip': '表の転置',
     }
     const optionNameKeys = Object.keys(optionName);

     /**/

     let lastInput;

     function toClipboard(text) {
         navigator.clipboard.writeText(text).then(() => {
             if (text.length >= 60)
                 text = text.slice(0, 57) + '⋅⋅⋅';
             tagClipboard.innerText = text;
         }, () => {
             tagClipboard.innerText = 'コピー失敗';
         });
     }
     const tagToClipboard = (tag => toClipboard(tag.innerText));

     function updateViewer() {
         const color_dis = 'lightgray';

         const getTag = (n => document.getElementById(n));
         const setfmt = ((m, n) => {
             const tagTd = getTag(`tagMode_${n}`);
             const tagInp = getTag(`chkbox_${n}`);
             tagInp.checked = viewOption[n];
             tagInp.disabled = !m;
             tagTd.style.backgroundColor = m ? null : color_dis;
         });

         viewOption['fbin'] = binaryList.filter(n => viewOption.bin && viewOption[`bin${n}`]);
         binaryList.forEach(n => setfmt(viewOption.bin, `bin${n}`));

         [['val', valueNameKeys],
          ['opt', optionNameKeys],
         ].forEach(l => {
             const [pfx, keys] = l;
             keys.forEach(k => {
                 const key = `${pfx}${k}`;
                 const tagName = getTag(`chkbox_${key}`);
                 tagName.checked = viewOption[key];
             });
         });
     }

     /**/

     const genCbTag = ((tag, cls, txt) =>
         `<${tag} class="${cls}" onclick="toClipboard('${txt}')">${txt}</${tag}>`
     );
     const genCodeCbTag = ((cls, txt) => genCbTag('code', cls, txt));

     function getValueNameList() {
         return valueNameKeys.map(
             k => viewOption[`val${k}`] ? valueName[k] : null
         ).filter(v=>v);
     }
     function getData(inp) {
         const rnum = new FractionBI(inp);
         const rnd0 = viewOption.optrnd0;
         return {
             inp: inp,
             bin: {
                 height: 5,
                 list: viewOption.fbin.map(v => {
                     const bin = new IEEE754Binary(v, rnum, rnd0);
                     return {
                         data: bin,
                         tag: [
                             viewOption.valdat ? ['ctext htext', bin.toHexData()] : null,
                             viewOption.valsem ? ['ctext', bin.toSepSignExpMan()] : null,
                             viewOption.valhex ? ['ctext', bin.toString16()] : null,
                             viewOption.valexp ? ['ctext', bin.toExponential()] : null,
                             viewOption.valdec ? ['ctext', bin.toString10()] : null,
                             viewOption.valxct ? ['ctext ftext', bin.toString10F()] : null,
                         ].filter(v=>v).map(l => genCodeCbTag(l[0], l[1])),
                     }
                 }),
             },
         }
     }

     function genOutputBinBlock1(nlist) {
         const binf = nlist[0].bin; if (!binf.list.length) return '';
         const getBinTag = ((i, b, n) => nlist[i].bin.list[b].tag[n]);
         const height = binf.height;
         const cline = ['eline', 'oline'];
         const tagHeader = getValueNameList();
         return [
             '<tr>',
             '<th>形式</th>',
             viewOption.fbin.map(n => `<th>binary${n}</th>`),
             '</tr>',
             nlist.map((s, i) => [
                 '<tr class="inpdata">',
                 '<th style="min-width: 3em;">入力</th>',
                 `<td class="ntext" colspan="${viewOption.fbin.length}">`,
                 genCodeCbTag('ctext large', s.inp),
                 '</td>',
                 '</tr>',
                 tagHeader.map((s, n) => [
                     `<tr class="${cline[n & 1]}">`,
                     `<th class="small">${s}</th>`,
                     viewOption.fbin.map((_, b) => [
                         '<td class="vtop ntext">', getBinTag(i, b, n), '</td>',
                     ]),
                     '</tr>',
             ])]),
         ];
     }

     function genOutputBinBlock2(nlist) {
         const binf = nlist[0].bin; if (!binf.list.length) return '';
         const getBinTag = ((i, b, n) => nlist[i].bin.list[b].tag[n]);
         const height = binf.height;
         const cline = ['eline', 'oline'];
         const tagHeader = getValueNameList();
         return [
             '<tr>',
             '<th>形式</th>',
             tagHeader.map(n => `<th>${n}</th>`),
             '</tr>',
             nlist.map((s, i) => [
                 '<tr class="inpdata">',
                 '<th style="min-width: 3em;">入力</th>',
                 `<td class="ntext" colspan="${tagHeader.length}">`,
                 genCodeCbTag('ctext large', s.inp),
                 '</td>',
                 '</tr>',
                 viewOption.fbin.map((w, b) => [
                     `<tr class="${cline[b & 1]}">`,
                     `<th class="small">binary${w}</th>`,
                     tagHeader.map((_, n) => [
                         '<td class="vtop ntext">', getBinTag(i, b, n), '</td>',
                     ]),
                     '</tr>',
             ])]),
         ];
     }

     function onUpdate(inp) {
         inp = inp ?? tagNumber.innerText;
         const nums = [...'\n ;,'].reduce(
             (p, c) => p.map(s => s.split(c)).flat(), [inp]
         ).filter(v => v != null && v.length);
         if (!nums.length)
             return;
         lastInput = inp;

         const nlist = nums.map(s => getData(s));
         const bintags = (viewOption.optflip ? genOutputBinBlock2 : genOutputBinBlock1)(nlist);

         tagOutput.innerHTML = [
             '<table>', bintags, '</table>', '<p></p>',
         ].flat(8).join('');
     }

     function onChangeViewer(tag) {
         viewOption[tag.id.split('_')[1]] = tag.checked;
         updateViewer();
         onUpdate(lastInput);
     }

     /*
      *
      */

     window.onload = (() => {
         const chkbox = ((n, l) => [
             `<input id="chkbox_${n}" name="chkbox_${n}" type="checkbox"`,
             ` onchange="onChangeViewer(this)">`,
             `<label id="label_${n}" for="chkbox_${n}">${l}</label>`,
         ]);

         const exact = ((n) => new IEEE754Binary64(n).toString16());

         document.body.innerHTML = [
             '<h3>IEEE 754 浮動小数点数 詳細表示</h3>',
             '<div><textarea',
             ' id="tagNumber"', ' cols="72"', ' rows="5"',
             ' placeholder="数値を入力してください"',
             ' oninput="onUpdate(value)">',
             '</textarea></div>',
             /**/
             '<details id="tagSettings"><summary>表示設定</summary>',
             '<table>',
             '<tr>', '<th>データ型</th>',
             Object.entries(binaryName).map(prec => {
                 const [b, l] = prec;
                 return [`<td id="tagMode_bin${b}">`, chkbox(`bin${b}`, l), `(${b})</td>`];
             }),
             '</tr>',
             '</table>',
             '<table style="margin-top: 2px;">',
             '<tr>', '<th>表示項目</th>',
             Object.entries(valueName).map(v => [
                 `<td id="tagView_val${v[0]}">`, chkbox(`val${v[0]}`, v[1]), '</td>'
             ]),
             '</tr>',
             '</table>',
             '<table style="margin-top: 2px;">',
             '<tr>', '<th>その他</th>',
             Object.entries(optionName).map(v => [
                 `<td id="tagOption_opt${v[0]}">`, chkbox(`opt${v[0]}`, v[1]), '</td>'
             ]),
             '</table>',
             '</details>',
             /**/
             '<div>&nbsp;</div>',
             '<div class="xsmall">クリップボード:<code id="tagClipboard">(数値をクリック)</code></div>',
             '<div>&nbsp;</div>',
             '<div id="tagOutput"></div>',
             '<div>&nbsp;</div>',
             '<table>',
             '<tr><th colspan="4">定数(binary64)</th></tr>',
             [['PI', 'LN2', 'LOG2E', 'SQRT1_2'],
              ['E', 'LN10', 'LOG10E', 'SQRT2'],
             ].map(l => [
                 '<tr>',
                 l.map(n => [
                     '<td style="padding: 2px;">',
                     `<button onclick="onUpdate('${exact(Math[n])}')">Math.${n}</button>`,
                     '</td>',
                 ]),
                 '</tr>',
             ]),
             '</table>',
         ].flat(9).join('');

         /*
          * URLの引数
          *
          * on=<type> または off=<type> で表示を制御
          *
          * <type>:
          *   setting: 表示設定
          *
          *   bin16: 2進数半精度
          *   bin32: 2進数単精度
          *   bin64: 2進数倍精度
          *   bin128: 2進数四倍精度
          *
          *   valdat: 記憶域
          *   valsem: 負:指:仮
          *   valhex: 16進
          *   valexp: 指数
          *   valdec: 非指数
          *   valxct: 厳密値
          *
          *   optrnd0: ゼロ方向丸め
          *   optflip: 表の転置
          */

         const set_flag = ((f, v) => {
             const p = v.slice(0, 3);
             if (viewList[p]) {
                 if (f)
                     viewOption[p] = f;
                 viewOption[v] = f;
                 return;
             }

             const s = v.slice(3);
             if (p == 'val') {
                 if (valueName[s])
                     viewOption[v] = f;
                 return;
             }
             if (p == 'opt') {
                 if (optionName[s])
                     viewOption[v] = f;
                 return;
             }
             if (v == 'setting') {
                 tagSettings.open = f;
                 return;
             }

             // ...
         });
         const set_flags = ((f, l) => l.forEach(v => set_flag(f, v)));

         /**/

         const url = new URL(window.location);
         const search = url.searchParams;

         const get_arglist = (l => l.map(n => search.getAll(n).map(v => v.split(','))).flat(5));
         const args_on = get_arglist(['on', 's']);
         const args_off = get_arglist(['off', 'u']);
         const args_inp = search.getAll('n').join('\n');

         set_flags(true, ['bin32', 'bin64']);
         set_flags(true, valueNameKeys.map(k => `val${k}`));

         set_flags(true, args_on);
         set_flags(false, args_off);

         updateViewer();
         onUpdate(args_inp.length ? args_inp : `${exact(Math.PI)}`);
     });

    </script>
  </body>
</html>
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?