Jhinel Arcaya

Posted on

# The day I almost made a library on a bakery

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
>>> 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:

``````
// 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
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.

Evan Oman • Edited

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!

Evan Oman • Edited

I thought I had implemented `Rational` before, here is my quick implementation for this Stack Overflow question:

``````scala> :pa
// Entering paste mode (ctrl-D to finish)

object Rational
{
def apply(n: BigInt, d: BigInt): Rational =
{
val neg_mod = if (d < BigInt(0)) BigInt(-1) else BigInt(1)
val (n_mod, d_mod) = (neg_mod * n, neg_mod * d)
val gcd_val = gcd(n_mod, d_mod)
new Rational(n_mod / gcd_val, d_mod / gcd_val)
}
def gcd(a: BigInt, b: BigInt): BigInt = if (b == BigInt(0)) a else gcd(b, a % b)
}
class Rational(val n: BigInt, val d: BigInt)
{
override def toString: String = if (n == BigInt(0)) "0" else if (d == BigInt(1)) s"\$n" else s"\$n/\$d"

def toDouble: Double = n.toDouble / d.toDouble

def *(that: Rational): Rational = Rational(n * that.n, d * that.d)

def /(that: Rational): Rational = Rational(n * that.d, d * that.n)

def +(that: Rational): Rational = Rational(n * that.d + that.n * d, d * that.d)

def -(that: Rational): Rational = this + (-that)

def unary_- = Rational(-n, d)
}

// Exiting paste mode, now interpreting.

defined object Rational
defined class Rational

scala> val a = Rational(60075, 10000)
a: Rational = 2403/400

scala> val b = Rational(1, 1)
b: Rational = 1

scala> val c = Rational(89, 100)
c: Rational = 89/100

scala> a * (b / c)
res0: Rational = 27/4

scala> (a * (b / c)).toDouble
res1: Double = 6.75
``````

Ben Halpern

Nice

Jhinel Arcaya

Ohh, I didn't think about it, and yes, they are only rational numbers. Thank you Evan :D

Theofanis Despoudis

This `getExponential` function name is confusing as it actually returns the length of the exponential part. A better name would be `getExponentialLength` or `getExponentialPartLength`.

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.

Ben Halpern

Great stuff Jhinel!

Jhinel Arcaya

Thank you! :D