--- title: Rational Approximation pagination_prev: constellation/ssf pagination_next: constellation/crc32 --- The SheetJS `frac` library computes rational approximations to floating point numbers with bounded denominator. It is a core component in number formatting, powering "Fraction with up to 1 digit" and related number formats. The library is also available for standalone use on the SheetJS CDN[^1]. Source code and project documentation are hosted on the SheetJS Git server at https://git.sheetjs.com/sheetjs/frac ## Live Demo Formatted texts are calculated from the value and maximum denominators. Please [report an issue](https://git.sheetjs.com/sheetjs/frac/issues) if a particular result does not align with expectations. ```jsx live function SheetJSFrac() { const [val, setVal] = React.useState(0.699450515); const [text, setText] = React.useState(""); if(typeof frac == "undefined") return ( ERROR: Reload this page ); const fmt = arr => `${(""+arr[1]).padStart(5)} / ${(""+arr[2]).padEnd(5)}`; React.useEffect(() => { if(typeof frac == "undefined") return setText("ERROR: Reload this page!"); let v = +val; if(!isFinite(v)) return setText(`ERROR: ${val} is not a valid number!`); try { fmt(frac(val, 9)); setText(""); } catch(e) { setText("ERROR: " + (e && e.message || e)); } }, [val]); const n = { textAlign:"right" }; const g = { backgroundColor:"#C6EFCE", color:"#006100", whiteSpace:"pre-wrap" }; const b = { backgroundColor:"#FFC7CE", color:"#9C0006" }; return ( {[1,2,3,4,5].map(d => ( ))}
Number Value setVal(e.target.value)}/>
Max DenomMediantContinued Frac
Up to {d} Digit{d == 1 ? "" : "s"} {10**d - 1} {text||fmt(frac(val,10**d-1))} {text||fmt(frac.cont(val,10**d-1))}
); } ``` ## API In the browser, the library exports the `frac` global. In NodeJS, the library default export is a function. #### Algorithms The "Mediant" algorithm (`frac` in the browser; the default export in NodeJS) calculates the exact solution. The "Continued Fractions" algorithm (`frac.cont` in the browser; the `cont` field in the NodeJS export) calculates an approximate solution but has better worst-case runtime performance. Spreadsheet software use these algorithms to render number formats including `?/?` and `??/??`. The algorithm choices are summarized in the following table: | Spreadsheet Software | Algorithm | |:-----------------------------------|:--------------------| | [SheetJS](/docs/constellation/ssf) | Continued Fractions | | Apple Numbers | Mediant Algorithm | | Google Sheets | Mediant Algorithm | | Lotus 1-2-3 | (unsupported) | | Microsoft Excel | Continued Fractions | | Quattro Pro | Continued Fractions | | WPS 电子表格 | Mediant Algorithm | :::danger LibreOffice bugs There are known rounding errors in LibreOffice[^2] which result in inaccurate fraction calculations. The LibreOffice developers believe these numerical errors are desirable: > "We ignore the last two bits for many stuff to improve the user experience." It is strongly recommended to use a different spreadsheet tool for accurate data processing involving fractions and numeric data. ::: #### Functions Both functions accept three arguments: ```js var frac_mediant = frac(value, denominator, mixed); var frac_cont = frac.cont(value, denominator, mixed); ``` - `value`: original value - `D`: maximum denominator (e.g. 99 = "Up to 2 digits") - `mixed`: if `true`, return a mixed fraction. The return value is an array with three integers: ```js var [ int, num, den ] = result; ``` - `int` (first element) represents the integer part of the estimate. - `num` (second element) is the numerator of the fraction - `den` (second element) is the positive denominator of the fraction The estimate can be recovered from the array: ```js var estimate = int + num / den; ``` If `mixed` is `false`, then `int = 0` and `0` < `den` ≤ `D` If `mixed` is `true`, then `0` ≤ `num` < `den` ≤ `D` :::info Negative Values When `mixed` is true, `int` will be the floor of the result. For example, in ```js var result = frac( -0.125 , 9, true); ``` the result will be `[ -1, 7, 8 ]`. This is interpreted as ``` -0.125 ~ (-1) + (7/8) ``` ::: [^1]: See https://cdn.sheetjs.com/frac/ for more details. [^2]: See [issue #83511](https://bugs.documentfoundation.org/show_bug.cgi?id=83511) in the LibreOffice bug tracker.