DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Cover image for The day I almost made a library on a bakery
Jhinel Arcaya
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
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)

Collapse
 
evanoman profile image
Evan Oman • Edited on

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!

Collapse
 
evanoman profile image
Evan Oman • Edited on

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
Collapse
 
ben profile image
Ben Halpern

Nice

Collapse
 
jhia profile image
Jhinel Arcaya

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

Collapse
 
theodesp profile image
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.

Collapse
 
ben profile image
Ben Halpern

Great stuff Jhinel!

Collapse
 
jhia profile image
Jhinel Arcaya

Thank you! :D

Join us at DEV
Yes, this is technically an β€œad”, but really we just want to ask if you want to join DEV. We have 900k+ developers reading, posting, and enjoying community, and would love to have you. Β  Create an account and continue your coding journey.