## DEV Community # Multiplying numbers when they're strings

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 === '-';
let bneg = b === '-';

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;

if(am)
{
anum += am;
}

let bdecCount = 0;
let bnum = bm;

if(bm)
{
bdecCount = bm.length
bnum += bm;
}

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
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;
}
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!