First thing first, I am not an expert in this area. I was just guessing that maybe this way I could avoid installing a library for a simple operation with small numbers. If you are looking for answers I recommend you this cool floating-point guide. But I still wrote this, hoping you'd laugh the same way employeers did.
Sometimes coding is so easy as walk through a flower garden, sometimes is challenging, but there are times that it's just:
Python 3.6.3 (default, Oct 24 2017, 14:48:20)
[GCC 7.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 0.1 + 0.2
0.30000000000000004
I wasn't expecting that. Well, I'm lying, I really expected that. Because this "buggy" answer is just a common result of floating-point arithmetics.
First time, I encountered myself searching why is this happening in Python and how should I solve it (Decimal
package is your friend). But then, Node.js needs an answer too:
node v9.4.0
> 0.1 + 0.2
0.30000000000000004
I'm pretty sure there are libraries for JavaScript, but I don't want to add any other dependency to my package.json for a simple operation. There should be a tricky answer for this, I guess so. I grab my laptop and said goodbye for now.
Then I went to the bakery shop.
Buy groceries is relaxing, but with the bread is unique; the small backery at the corner has at least half of my summer playlist, being played as background music. Some clients and employers use to sing or at least whisper the songs. I am one of those. But that day was so full, so I decided to wait for my partner in one of the outside tables.
A friend of mine lent me Fictions from Jorge Luis Borges, I started to read with this relaxing music. Some lines of the second chapter of Tlön, Uqbar, Orbis Tertius mention about duodecimal and sexagesimal conversions. At this point, an idea passed through my inspired mind. I stopped and closed the book.
What if just, instead of process this numbers by default (binary fraction), transform numbers into integers?
Computers are better calculating integers, and then turning back the result to decimals...
Eureka!
I didn't realize I was thinking out loud. Everyone turned his head to me, surprised, of course. In reaction, I started to sing and dance after "that's my song!". Well, the show must continue. And then surprise turned into laughs.
Thank you, Shakira.
Back to my sit, and embarrassed for my scene, I sketch what I will code in the next lines:
We need to know how many decimals has this number:
function getExponential(num) {
if (!!(num % 1)) {
return num.toString().split('.')[1].length;
}
// just for integers
return 0;
}
And then we can make a function to calculate the addition of two numbers like:
function add(a, b) {
// we use the greater exponent
const exp = Math.max(getExponential(a), getExponential(b));
const intConversor = Math.pow(10, exp);
return (a * intConversor + b * intConversor) / intConversor;
}
With subtraction is almost the same:
function subtract(a, b) {
// we use the greater exponent
const exp = Math.max(getExponential(a), getExponential(b));
const intConversor = Math.pow(10, exp);
return (a * intConversor - b * intConversor) / intConversor;
}
The number of decimals for a
times b
, is result of the sum of decimal's length for both numbers. Both numbers should be the minimum expresion of integers they could be:
function multiply(a, b) {
const expA = getExponential(a);
const expB = getExponential(b);
const floatConversor = Math.pow(10, expA + expB);
return (a * Math.pow(10, expA)) * (b * Math.pow(10, expB)) / floatConversor;
}
I'm thinking about decimals with division:
function divide(a, b) {
// we use the greater exponent
const exp = Math.max(getExponential(a), getExponential(b));
const intConversor = Math.pow(10, exp);
return a * intConversor / (b * intConversor);
}
Let's test it out:
node v9.4.0
> add(0.1, 0.2)
0.3
> subtract(0.1, 0.2)
-0.1
> multiply(0.1, 0.2)
0.02
> divide(0.1, 2)
0.05
Done! Finally, I can make operations without using any other external library. Mission accomplished. Time to get a reward, coffee! But, of course, so far bakery shop.
Lead photo by Jesse Milns at Sud Forno.
Top comments (7)
If you are willing to restrict your domain to rational numbers, a simple implementation of a
Rational
type is another good solution. Store the numerator and denominator and add a least-common-multiple implementation (for finding the common denominator) and you are off to the races!I thought I had implemented
Rational
before, here is my quick implementation for this Stack Overflow question:Nice
Ohh, I didn't think about it, and yes, they are only rational numbers. Thank you Evan :D
This
getExponential
function name is confusing as it actually returns the length of the exponential part. A better name would begetExponentialLength
orgetExponentialPartLength
.Also this line
const intConversor = Math.pow(10, exp)
can quickly turn into infinity or integer overflow(> MAX_SAFE_INTEGER is the upper limit) so almost all operations will return the same number.Great stuff Jhinel!
Thank you! :D