Skip to content

Instantly share code, notes, and snippets.

@jbruni
Last active September 25, 2018 22:07
Show Gist options
  • Save jbruni/3004a783369a60931e2baf644440d129 to your computer and use it in GitHub Desktop.
Save jbruni/3004a783369a60931e2baf644440d129 to your computer and use it in GitHub Desktop.
Math functions suitable for monetary calculations
/**
* Basic Arithmetic
*/
const bind = Function.prototype.bind,
liberate = bind.bind(Function.prototype.call),
reduce = liberate(Array.prototype.reduce),
slice = liberate(Array.prototype.slice);
function multiplier(x) {
const parts = x.toString().split('.');
if (parts.length < 2) {
return 1;
}
return Math.pow(10, parts[1].length);
}
function correctionFactor() {
return reduce(arguments, function (prev, next) {
const mp = multiplier(prev),
mn = multiplier(next);
return mp > mn ? mp : mn;
}, -Infinity);
}
export function add() {
var corrFactor = correctionFactor.apply(null, arguments);
function cback(accum, curr, currI, O) {
return accum + corrFactor * curr;
}
return reduce(arguments, cback, 0) / corrFactor;
}
export function sub() {
var corrFactor = correctionFactor.apply(null, arguments),
first = arguments[0];
function cback(accum, curr, currI, O) {
return accum - corrFactor * curr;
}
delete arguments[0];
return reduce(arguments,
cback, first * corrFactor) / corrFactor;
}
export function mul() {
function cback(accum, curr, currI, O) {
var corrFactor = correctionFactor(accum, curr);
return (accum * corrFactor) * (curr * corrFactor) /
(corrFactor * corrFactor);
}
return reduce(arguments, cback, 1);
}
export function div() {
function cback(accum, curr, currI, O) {
var corrFactor = correctionFactor(accum, curr);
return (accum * corrFactor) / (curr * corrFactor);
}
return reduce(arguments, cback);
}
// Taken from https://github.com/guipn/sinful.js
void function (bless) {
'use strict';
var bind = Function.prototype.bind,
liberate = bind.bind(Function.prototype.call),
reduce = liberate(Array.prototype.reduce),
slice = liberate(Array.prototype.slice);
bless = bless || function (thing, name, content) {
if (typeof thing[name] !== 'undefined') {
throw new Error('Sinful: ' + name + ' is already defined.');
}
thing[name] = content;
};
function multiplier(x) {
var parts = x.toString().split('.');
if (parts.length < 2) {
return 1;
}
return Math.pow(10, parts[1].length);
}
function correctionFactor() {
return reduce(arguments, function (prev, next) {
var mp = multiplier(prev),
mn = multiplier(next);
return mp > mn ? mp : mn;
}, -Infinity);
}
[
[Math, 'add', function () {
var corrFactor = correctionFactor.apply(null, arguments);
function cback(accum, curr, currI, O) {
return accum + corrFactor * curr;
}
return reduce(arguments, cback, 0) / corrFactor;
}],
[Math, 'sub', function () {
var corrFactor = correctionFactor.apply(null, arguments),
first = arguments[0];
function cback(accum, curr, currI, O) {
return accum - corrFactor * curr;
}
delete arguments[0];
return reduce(arguments,
cback, first * corrFactor) / corrFactor;
}],
[Math, 'mul', function () {
function cback(accum, curr, currI, O) {
var corrFactor = correctionFactor(accum, curr);
return (accum * corrFactor) * (curr * corrFactor) /
(corrFactor * corrFactor);
}
return reduce(arguments, cback, 1);
}],
[Math, 'div', function () {
function cback(accum, curr, currI, O) {
var corrFactor = correctionFactor(accum, curr);
return (accum * corrFactor) / (curr * corrFactor);
}
return reduce(arguments, cback);
}],
].forEach(function (blessing) {
bless(blessing.shift(), blessing.shift(), blessing.shift());
});
}();
@jbruni
Copy link
Author

jbruni commented Sep 25, 2018

Functions taken from sinful.js project.

Documentation


Math

Basic Arithmetic

ECMAScript performs floating point arithmetic to compute +, -, * and /. You should understand why this is a problem. Sinful gives the Math object the add, sub, mul, div and intdiv properties, whose values are all functions designed to perform fundamental arithmetic that is free from the problems of floating point representation (such as the fact that 0.1 + 0.2 !== 0.3). Examples:

Math.add(0.1, 0.2); // ↦ 0.3, instead of 0.30000000000000004
Math.sub(0.3, 0.2); // ↦ 0.1, instead of 0.09999999999999998
Math.mul(0.2, 0.1); // ↦ 0.02, instead of 0.020000000000000004
Math.div(0.3, 0.1); // ↦ 3, instead of 2.9999999999999996

@jbruni
Copy link
Author

jbruni commented Sep 25, 2018

I have removed the wrapping code and the Math prototype changes from the original sinful.js code.

The result is at basic-math.js file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment