Skip to content

Instantly share code, notes, and snippets.

@pbatey
Last active January 23, 2021 23:21
Show Gist options
  • Save pbatey/626d4f086ea64cab3c22f149900097e2 to your computer and use it in GitHub Desktop.
Save pbatey/626d4f086ea64cab3c22f149900097e2 to your computer and use it in GitHub Desktop.
Show number as fraction in javascript
/**
* Render a number as a fraction
* @param {number} v the decimal value to convert to a fraction
* @param {number[]} denom the list of denominators
* @returns {string} the fraction closest to the decimal value
*/
const toFraction = (v, denom=[2,3,4,7,8,10,16,32,64,100]) => {
denom = denom.sort((a,b)=>a-b)
// whole and fractional parts
var w = Math.trunc(v)
const f = Math.abs(v - w)
// start with the max denom
var d = denom[denom.length-1]
var n = Math.round(d*f)
var delta = Math.abs(f - n/d)
// check if other denom are closer
for (var i=0; i < denom.length-1; i++) {
const d1 = denom[i]
const x = d1*f
const n1 = Math.round(x)
const delta1 = Math.abs(f - n1/d1)
if (n1 && delta1 <= delta) {
d = d1
n = n1
delta = delta1
break
}
}
if (n == d) {
w++
n = 0
}
const result = []
if (w !== 0) result.push(w)
if (n > 0) result.push(`${n}/${d}`)
return result.join(' ')
}
export default toFraction
import toFraction from './toFraction.mjs'
const data = [
[0.004, '' ], // close to 0
[0.005, '1/100' ], // close to 0
[0.994, '99/100' ], // close to 1
[0.995, '1' ], // close to 1
[0.01, '1/100'],
[0.015625, '1/64'], // halfway between 1/100 and 1/64 - rounds up
[0.016, '1/64'],
[0.017, '1/64'],
[(.015625 + .02)/2, '2/100'], // halfway between 1/64 and 2/100 - rounds up
[0.018, '2/100'],
[0.02, '2/100'],
[0.03, '3/100'],
[(.03+1/32)/2, '1/32'], // halfway between 3/100 and 1/32
[0.03125, '1/32'],
[0.035625, '1/32'],
[(1/32 + .04)/2, '4/100'], // halfway between 1/32 and 4/100
[0.04, '4/100'],
[0.05, '5/100'],
[0.06, '6/100'],
[0.06125, '1/16'], // halfway between 6/100 and 1/16 - rounds up
[0.0625, '1/16'],
[0.06625, '1/16'], // halfway between 1/16 and 7/100 - rounds down
[0.07, '7/100'],
[0.08, '8/100'],
[0.09, '9/100'],
[0.1, '1/10'],
[(.1 + 1/7)/2, '12/100'],
[1/7, '1/7'],
[(1/7 + 1/8)/2, '13/100'],
[0.125, '1/8'],
[0.2, '2/10'],
[0.25, '1/4'],
[0.3125, '5/16'],
[0.33, '33/100'],
[0.3316, '33/100'],
[(.33 + (1/3))/2, '1/3'], // halfway between 33/100 and 1/3 - rounds up
[(1/3), '1/3'],
[((1/3) + .34)/2, '1/3'], // halfway between 1/3 and 34/100 - rounds down
[.3367, '34/100'],
[0.34, '34/100'],
[5/8, '5/8'],
[11/16, '11/16'],
[23/32, '23/32'],
[47/64, '47/64'],
[0.375, '3/8'],
[0.5, '1/2'],
[2/3, '2/3'],
[0.75, '3/4'],
[Math.PI, '3 1/7'],
[1.3333, '1 1/3'],
[-1.3333, '-1 1/3'],
[Math.PI, '3 16/113', [7,100,113,1000000]], // 355/113 is closer to PI than 3.141592, but not 3.1415926
]
const negative = x => `\x1b[0;31m${x}\x1b[0;m`
const positive = x => `\x1b[0;32m${x}\x1b[0;m`
var failures = 0
data.forEach(d => {
const [v,e, denom] = d
const f = toFraction(v, denom)
console.log('toFraction(',v,'): expect',f,'to be',e, f === e ? positive('pass') : negative('fail'))
failures += f === e ? 0 : 1
})
const vomit = '🤮'
const cheers = '🍻'
const passed = data.length - failures
console.log( // N passed, N failed emoji
passed, passed > 0 ? `${positive('passed')},` : 'passed,',
failures, failures > 0 ? negative('failed') : 'failed',
failures > 0 ? vomit : cheers)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment