{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "# Collect value statistics for formats\n",
    "\n",
    "This notebook computes various statistics for a variety of float formats,\n",
    "by exhaustively enumerating the values.  Naturally, most of these statistics can be computed directly, and indeed many are already supplied on the `FormatInfo` class as methods, for example `max`, `smallest_subnormal`, etc.  However this method serves as a useful cross-check against the direct formulae.\n",
    "\n",
    "## Statistics collected\n",
    "\n",
    " - name: Format\n",
    " - B: Bits in the format\n",
    " - P: Precision in bits\n",
    " - E: Exponent field width in bits\n",
    " - T: Trailing significand field width in bits\n",
    " - lt1: Number of values x such that `0 < x < 1`\n",
    " - gt1: Number of values x such that `1 < x < Inf`\n",
    " - rt16: True if all values are exactly representable in IEEE binary16\n",
    " - maxFinite: Largest finite value\n",
    " - minFinite: Smallest finite value\n",
    " - maxNormal: Largest finite normal value, NaN if all finite values are subnormal\n",
    " - minNormal: Smallest positive normal value, NaN if all finite values are subnormal\n",
    " - minSubnormal: Smallest positive subnormal value, NaN if no finite values are  - subnormal\n",
    " - maxSubnormal: Largest subnormal value, NaN if no finite values are subnormal\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from gfloat import *\n",
    "from gfloat.formats import *\n",
    "\n",
    "import pandas\n",
    "import numpy as np\n",
    "from IPython.display import HTML\n",
    "\n",
    "\n",
    "def collect_stats(fi: FormatInfo):\n",
    "    # Generate all values\n",
    "    values = [decode_float(fi, i) for i in range(2**fi.bits)]\n",
    "    df = pandas.DataFrame(values)\n",
    "\n",
    "    # Extract format information parameters\n",
    "    E = fi.expBits\n",
    "    S = fi.tSignificandBits\n",
    "\n",
    "    # Compute statistics: lt1,gt1\n",
    "    fval = df[\"fval\"]\n",
    "    total_01 = fval.between(0, 1, inclusive=\"neither\").sum()\n",
    "    total_1Inf = fval.between(1, np.inf, inclusive=\"neither\").sum()\n",
    "\n",
    "    # Compute statistics: maxFinite,minFinite\n",
    "    finite_vals = fval[np.isfinite(fval)]\n",
    "    maxFinite = finite_vals.loc[finite_vals.idxmax()]\n",
    "    minFinite = finite_vals.loc[finite_vals.idxmin()]\n",
    "\n",
    "    # Compute statistics: maxNormal,minNormal\n",
    "    normal_vals = fval[(df[\"fclass\"] == FloatClass.NORMAL) & (fval > 0)]\n",
    "    maxNormal = normal_vals.loc[normal_vals.idxmax()] if normal_vals.any() else np.nan\n",
    "    minNormal = normal_vals.loc[normal_vals.idxmin()] if normal_vals.any() else np.nan\n",
    "\n",
    "    # Compute statistics: minSubnormal\n",
    "    pos_subnormal = fval[(df[\"fclass\"] == FloatClass.SUBNORMAL) & (fval > 0)]\n",
    "    maxSubnormal = (\n",
    "        pos_subnormal.loc[pos_subnormal.idxmax()] if pos_subnormal.any() else np.nan\n",
    "    )\n",
    "    minSubnormal = (\n",
    "        pos_subnormal.loc[pos_subnormal.idxmin()] if pos_subnormal.any() else np.nan\n",
    "    )\n",
    "\n",
    "    # Compute roundtrips: rt16, rt32\n",
    "    with np.errstate(over=\"ignore\"):\n",
    "        rt16 = (np.float64(np.float16(fval)) == np.float64(fval)) | ~np.isfinite(fval)\n",
    "        rt32 = (np.float64(np.float32(fval)) == np.float64(fval)) | ~np.isfinite(fval)\n",
    "\n",
    "    rt16 = rt16.all()\n",
    "    rt32 = rt32.all()\n",
    "    assert rt32  # If not, we should include rt32 in the table\n",
    "\n",
    "    # Assemble tuple\n",
    "    return dict(\n",
    "        name=fi.name,\n",
    "        B=fi.bits,\n",
    "        P=fi.precision,\n",
    "        E=E,\n",
    "        T=S,\n",
    "        lt1=total_01,\n",
    "        gt1=total_1Inf,\n",
    "        rt16=rt16,\n",
    "        maxFinite=maxFinite,\n",
    "        minFinite=minFinite,\n",
    "        maxNormal=maxNormal,\n",
    "        minNormal=minNormal,\n",
    "        minSubnormal=minSubnormal,\n",
    "        maxSubnormal=maxSubnormal,\n",
    "    )\n",
    "\n",
    "\n",
    "formats_to_check = (\n",
    "    tiny_formats\n",
    "    + fp8_formats\n",
    "    + [format_info_bfloat16, format_info_ocp_int8, format_info_ocp_e8m0]\n",
    ")\n",
    "stats = [collect_stats(fi) for fi in formats_to_check]\n",
    "df = pandas.DataFrame(stats)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Emit HTML table"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "
\n",
       "  
\n",
       "    \n",
       "       name\n",
       "       B\n",
       "       P\n",
       "       E\n",
       "       T\n",
       "       lt1\n",
       "       gt1\n",
       "       rt16\n",
       "       maxFinite\n",
       "       minFinite\n",
       "       maxNormal\n",
       "       minNormal\n",
       "       minSubnormal\n",
       "       maxSubnormal\n",
       "    
\n",
       "  
\n",
       "  
\n",
       "    \n",
       "       ocp_e2m1\n",
       "       4\n",
       "       2\n",
       "       2\n",
       "       1\n",
       "       1\n",
       "       5\n",
       "       True\n",
       "              6\n",
       "             -6\n",
       "              6\n",
       "              1\n",
       "            0.5\n",
       "            0.5\n",
       "    
\n",
       "    \n",
       "       ocp_e2m3\n",
       "       6\n",
       "       4\n",
       "       2\n",
       "       3\n",
       "       7\n",
       "       23\n",
       "       True\n",
       "            7.5\n",
       "           -7.5\n",
       "            7.5\n",
       "              1\n",
       "          0.125\n",
       "          0.875\n",
       "    
\n",
       "    \n",
       "       ocp_e3m2\n",
       "       6\n",
       "       3\n",
       "       3\n",
       "       2\n",
       "       11\n",
       "       19\n",
       "       True\n",
       "             28\n",
       "            -28\n",
       "             28\n",
       "           0.25\n",
       "         0.0625\n",
       "         0.1875\n",
       "    
\n",
       "    \n",
       "       ocp_e4m3\n",
       "       8\n",
       "       4\n",
       "       4\n",
       "       3\n",
       "       55\n",
       "       70\n",
       "       True\n",
       "            448\n",
       "           -448\n",
       "            448\n",
       "       0.015625\n",
       "       1*2^-9\n",
       "       7/4*2^-7\n",
       "    
\n",
       "    \n",
       "       ocp_e5m2\n",
       "       8\n",
       "       3\n",
       "       5\n",
       "       2\n",
       "       59\n",
       "       63\n",
       "       True\n",
       "          57344\n",
       "         -57344\n",
       "          57344\n",
       "       1*2^-14\n",
       "       1*2^-16\n",
       "       3/2*2^-15\n",
       "    
\n",
       "    \n",
       "       p3109_p1\n",
       "       8\n",
       "       1\n",
       "       7\n",
       "       0\n",
       "       62\n",
       "       63\n",
       "       False\n",
       "       1*2^63\n",
       "       -1*2^63\n",
       "       1*2^63\n",
       "       1*2^-62\n",
       "       nan\n",
       "       nan\n",
       "    
\n",
       "    \n",
       "       p3109_p2\n",
       "       8\n",
       "       2\n",
       "       6\n",
       "       1\n",
       "       63\n",
       "       62\n",
       "       False\n",
       "       1*2^31\n",
       "       -1*2^31\n",
       "       1*2^31\n",
       "       1*2^-31\n",
       "       1*2^-32\n",
       "       1*2^-32\n",
       "    
\n",
       "    \n",
       "       p3109_p3\n",
       "       8\n",
       "       3\n",
       "       5\n",
       "       2\n",
       "       63\n",
       "       62\n",
       "       True\n",
       "          49152\n",
       "         -49152\n",
       "          49152\n",
       "       1*2^-15\n",
       "       1*2^-17\n",
       "       3/2*2^-16\n",
       "    
\n",
       "    \n",
       "       p3109_p4\n",
       "       8\n",
       "       4\n",
       "       4\n",
       "       3\n",
       "       63\n",
       "       62\n",
       "       True\n",
       "            224\n",
       "           -224\n",
       "            224\n",
       "       0.0078125\n",
       "       1*2^-10\n",
       "       7/4*2^-8\n",
       "    
\n",
       "    \n",
       "       p3109_p5\n",
       "       8\n",
       "       5\n",
       "       3\n",
       "       4\n",
       "       63\n",
       "       62\n",
       "       True\n",
       "             15\n",
       "            -15\n",
       "             15\n",
       "          0.125\n",
       "       0.0078125\n",
       "       15/8*2^-4\n",
       "    
\n",
       "    \n",
       "       p3109_p6\n",
       "       8\n",
       "       6\n",
       "       2\n",
       "       5\n",
       "       63\n",
       "       62\n",
       "       True\n",
       "          3.875\n",
       "         -3.875\n",
       "          3.875\n",
       "            0.5\n",
       "       0.015625\n",
       "       31/16*2^-2\n",
       "    
\n",
       "    \n",
       "       bfloat16\n",
       "       16\n",
       "       8\n",
       "       8\n",
       "       7\n",
       "       16255\n",
       "       16383\n",
       "       False\n",
       "       255/128*2^127\n",
       "       -255/128*2^127\n",
       "       255/128*2^127\n",
       "       1*2^-126\n",
       "       1*2^-133\n",
       "       127/64*2^-127\n",
       "    
\n",
       "    \n",
       "       ocp_int8\n",
       "       8\n",
       "       8\n",
       "       0\n",
       "       7\n",
       "       63\n",
       "       63\n",
       "       True\n",
       "       127/64*2^0\n",
       "             -2\n",
       "       nan\n",
       "       nan\n",
       "       0.015625\n",
       "       127/64*2^0\n",
       "    
\n",
       "    \n",
       "       ocp_e8m0\n",
       "       8\n",
       "       1\n",
       "       8\n",
       "       0\n",
       "       127\n",
       "       127\n",
       "       False\n",
       "       1*2^127\n",
       "       1*2^-127\n",
       "       1*2^127\n",
       "       1*2^-127\n",
       "       nan\n",
       "       nan\n",
       "    
\n",
       "  
\n",
       "
\n"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Special rendering for float values - if they don't render nicely in 10.5g,\n",
    "# use float_pow2str\n",
    "def render_float(v):\n",
    "    s = f\"{v:8.5g}\"\n",
    "    if not \"e\" in s and float(s) == v:\n",
    "        return s\n",
    "    else:\n",
    "        return float_pow2str(v)\n",
    "\n",
    "\n",
    "for field in (\n",
    "    \"maxFinite\",\n",
    "    \"minFinite\",\n",
    "    \"maxNormal\",\n",
    "    \"minNormal\",\n",
    "    \"minSubnormal\",\n",
    "    \"maxSubnormal\",\n",
    "):\n",
    "    df[field] = df[field].map(render_float)\n",
    "\n",
    "\n",
    "HTML(df.style.hide().to_html())"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
