We all know it: javascript's numbers are a long standing meme in the programming community.

There are work arounds, such as using a delta when comparing two floating point numbers

```
if(Math.abs(a-d) < 0.0001)
{
//these are close enough to be called the same
}
```

Or fixes, such as the BigInt class:

```
let toobig1 = 9007199254740992n;
let toobig2 = 9007199254740993n;
console.log(toobig1 == toobig2) //false! unlike for regular numbers
```

So we can work with the limitations that IEEE floating point numbers impose upon us.

But just for fun, I want to show how to multiply two arbitrary floating point numbers when they are strings.

The method I will use is basically what I was taught in school, e.g.:

```
123
×456
------
738
+ 6150
+49200
------
56088
```

There is a limit to this, because the ECMAScript specification has a maximum string length of 2**53 - 1 (i.e. 9007199254740991) and some browsers implement an even stricter limit. Firefox, for example, limits string length to 2**30 - 2 (i.e. 1073741822), but in theory this methods can be used with any two numbers with any number of digits each.

Now, I know *you* would never put in invalid input, and *I* certainly would not, but just in case some imbecile uses the function I'm defining a number as anything that matches this regular expression: `/^(-?)(\d+)(([.])(\d+))?$/`

Which means that there always has to be a number before the decimal place, so this function would reject `.2`

, which might annoy some people, but I'm doing for the sake of simplicity. Also, no thousands separators or similar allowed, and I'm ignoring the fact that some localities use `,`

as the decimal place, and assuming everything is written left to right. I leave all those non-mathematical parts as an exercise to the reader.

All the grouping is so I can use the separate bits.

So, up top, the function looks like:

```
let am = a.match(/^(-?)(\d+)(([.])(\d+))?$/)
if(am === null)
{
throw `Format Error: ${a} is not a valid number`
}
let bm = b.match(/^(-?)(\d+)(([.])(\d+))?$/)
if(bm === null)
{
throw `Format Error: ${b} is not a valid number`
}
```

Next I need to detect if the result will be negative.

```
let aneg = a[0] === '-';
let bneg = b[0] === '-';
let negative = (aneg ^ bneg) === 1;
```

`^`

is the XOR operator, and `true`

gets treated as `1`

and `false`

as `0`

.

I'm actually going to do integer multiplication and put the decimal place in afterwards. So the next thing I want to know is how many digits there will be after the decimal place. This is the sum of the number of digits after the decimal place in each number.

```
let adecCount = 0;
let anum = am[2];
if(am[5])
{
adecCount = am[5].length
anum += am[5];
}
let bdecCount = 0;
let bnum = bm[2];
if(bm[5])
{
bdecCount = bm[5].length
bnum += bm[5];
}
let finalDecCount = adecCount + bdecCount;
```

You can see I'm also mashing together the integer and fractional parts of each number.

Now I need to do each partial calculation, just in case I'm asked to show my working. Don't forget those carries!

```
let partresults = [];
let adigits = anum.split('').reverse().map(s => parseInt(s, 10));
let bdigits = bnum.split('').reverse().map(s => parseInt(s, 10));
for(let ai = 0; ai < adigits.length; ai++)
{
let part = (Array(ai)).fill(0);
let carry = 0
let da = adigits[ai];
for(let db of bdigits)
{
let mul = (db*da) + carry;
carry = Math.floor(mul/10);
mul = mul%10;
part.unshift(mul);
}
if(carry > 0)
{
part.unshift(carry);
}
partresults.push(part);
}
```

The first thing I do it turn the string of digits into an array of single digit numbers. I reverse the order because I want to work from right to left.

Personally I prefer `for`

loops over calling `.forEach`

, but that's just habit rather than any other reason.

The calculation has an outer loop and an inner loop.

The first thing I'm doing in the outer loop (`let part = (Array(ai)).fill(0);`

) is making sure that each partial calculation lines up the units, tens, hundreds, etc., correctly, with the units on the right.

Next I need to add each array in the array of arrays together, to end with one array which is the result. Sounds like a reduce operation if ever there was one.

```
let resultDigits = [];
if(partresults.length === 1)
{
resultDigits = partresults[0];
}
else
{
resultDigits = partresults.reduce((agg, arr) =>
{
while(agg.length < arr.length)
{
agg.unshift(0);
}
let carry = 0;
for(let arri = arr.length-1; arri >= 0; arri--)
{
let agd = agg[arri];
let ard = arr[arri];
let value = agd + ard + carry;
if(value > 9)
{
carry = Math.floor(value/10);
value = value % 10;
}
else
{
carry = 0;
}
agg[arri] = value;
}
if(carry > 0)
{
agg.unshift(carry);
}
return agg;
}, []);
}
```

Of course I need to deal with the simple case where there's only one nested array.

Now I need to figure out where the decimal place might go.

```
if(finalDecCount > 0)
{
resultDigits.splice(resultDigits.length - finalDecCount, 0, '.');
}
```

Ohh splice! How splicy.

And finally I add in a `-`

if the result if negative, join it all together and return.

```
if(negative)
{
resultDigits.unshift('-');
}
return resultDigits.join('');
```

You can see the full code in this gist.

Feel free to riff on it, and let me know optimisations or different approaches you'd take!

## Top comments (0)