24

Javascript has the function parseInt() which can help convert integer in a binary form into its decimal equivalent:

parseInt("101", 2) // 5

However, I need to convert binary fraction to its decimal equivalent, like:

0.101 = 0.625

I can write my own function that would calculate the result like the following:

1 * Math.pow(2, -1) + 0*Math.pow(2, -2) + 1*Math.pow(2, -3) // 0.625

But I'm wondering whether there is anything standard already.

Max Koretskyi
  • 101,079
  • 60
  • 333
  • 488
  • `parseInt()` converts a number in its *ASCII* form into its *binary* equivalent. What you're asking for is given by [`parseFloat()`](http://stackoverflow.com/q/21278234/207421). – user207421 May 09 '16 at 09:41
  • 3
    @EGP parseFloat() doesn't provide radix parameter for base exchange – granmirupa May 09 '16 at 10:29

6 Answers6

11

This question asks whether there is some JavaScript standard for parsing binary floating-point numbers, as if parseFloat() could have taken a second radix parameter to parse the binary number 0.101 like this: parseFloat('0.101', 2).

While there is no such standard, there is an easy and direct way to solve this.

If we represent the binary number 0.101 as a binary fraction, it is easily converted to a decimal fraction:

0.1012 = 1012/10002 = (5/8)10 = 0.625

The following one-line expression translates this to JavaScript. Here, num could be any type of binary number (represented as a string), including negative numbers:

parseInt(num.replace('.', ''), 2) / Math.pow(2, (num.split('.')[1] || '').length)

The solution is easily adapted to floating-point numbers in any base between 2 and 36, and we can wrap it in our own parseFloatRadix() function:

function parseFloatRadix(num, radix) {
  return parseInt(num.replace('.', ''), radix) /
    Math.pow(radix, (num.split('.')[1] || '').length)
}

test('0.101',  2, 0.625);
test('0.011',  2, 0.375);
test('0.0011', 2, 0.1875);
test('-011',   2, -3);
test('011',    2,  3);
test('-1100.0011', 2, -12.1875);
test('1100.0011',  2,  12.1875);
test('0.00011001100110011001100', 2, 0.09999990463256836);

test('ABC',     16, 2748);
test('-0.DEF',  16, -0.870849609375);
test('ABC.DEF', 16, 2748.870849609375);

test('-102.201', 3, -11.703703703703704);
test('-Z.ZZZ',  36, -35.99997856652949);

function test(num, radix, expected){
  let result = parseFloatRadix(num, radix);
  console.log(num + ' (base ' + radix +') --> ' + result + 
    (result === expected ? ' (OK)' : ' (Expected ' + expected + ')'));
}
Tomas Langkaas
  • 4,551
  • 2
  • 19
  • 34
10

You can split the number (as string) at the dot and treat the integer part with an own function and the fraction part with another function for the right value.

The solution works with other bases as well.

function convert(value, base = 2) {
    var [integer, fraction = ''] = value.toString().split('.');

    return parseInt(integer, base) + (integer[0] !== '-' || -1) * fraction
        .split('')
        .reduceRight((r, a) => (r + parseInt(a, base)) / base, 0);
}

console.log(convert(1100));           //    12
console.log(convert(0.0011));         //     0.1875
console.log(convert(1100.0011));      //    12.1875

console.log(convert('ABC', 16));      //  2748
console.log(convert('0.DEF', 16));    //     0.870849609375
console.log(convert('ABC.DEF', 16));  //  2748.870849609375

console.log(convert('-ABC.DEF', 16)); // -2748.870849609375
console.log(convert(-1100.0011));     //   -12.1875
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
6

TL;DR:

const convert = (s, b) => (+((s = s.toString().trim().split("."))[0][0] !== "-") || -1) * ((parseInt(s[0].replace("-", ""), (b = +b || 2))) + (s[1].split("").reduceRight((n, d) => (n + parseInt(d, b)) / b, 0)));

Nina Scholz has a nice example, but it doesn't work with negative numbers (plus other issues).
So, this is an improved one:

/**
 * @param {string} input
 * @param {number} [base]
 * @returns {number}
 */
function convert(input, base = 2) {
    const [ integerRaw, decimalRaw = "" ] = input.toString().trim().split(".");

    const integer = parseInt(integerRaw.replace("-", ""), base);
    const decimal = decimalRaw.split("").reduceRight((sum, num) => (sum + parseInt(num, base)) / base, 0);

    return (integerRaw.startsWith("-") ? -1 : 1) * (integer + decimal);
}

convert("1100.0011"); // 12.1875
convert("-1100.0011"); // -12.1875
convert("-Z.ZZZ", 36); // -35.99997856652949

As I know, JavaScript doesn't provide such built-in functionality.

Parzh from Ukraine
  • 7,999
  • 3
  • 34
  • 65
3

But I'm wondering whether there is anything standard already.

No according my knowledge

I think you need to create your own function:

  function toDecimal(string, radix) {
    radix = radix || 2;
    var s = string.split('.');
    var decimal = parseInt(s[0], radix);

    if(s.length > 1){
       var fract = s[1].split('');

       for(var i = 0, div = radix; i < fract.length; i++, div = div * radix) {
          decimal = decimal + fract[i] / div;
       }
    }
    return decimal;
  }
granmirupa
  • 2,780
  • 16
  • 27
2

You can create javascript extension method like parseInt as I have created method parseBinary which works just link parseInt.

String.prototype.parseBinary = function parseBinary() {
  var radix = 2;
  var s = this.split('.');
  var decimal = parseInt(s[0], radix);

  if(s.length > 1){
    var fract = s[1].split('');

    for(var i = 0, div = radix; i < fract.length; i++, div = div * radix)
    {
      decimal = decimal + fract[i] / div;
    }
  }
  return decimal;
};

To use this method you can call it using following code.

var str = "0.101";
alert(str.parseBinary());

Here is a working example of javascript code

String.prototype.parseBinary = function parseBinary() {
  var radix = 2;
  var s = this.split('.');
  var decimal = parseInt(s[0], radix);

  if(s.length > 1){
    var fract = s[1].split('');

    for(var i = 0, div = radix; i < fract.length; i++, div = div * radix)
    {
      decimal = decimal + fract[i] / div;
    }
  }
  return decimal;
};

var str = "0.101";
alert(str.parseBinary());
Umar Abbas
  • 4,399
  • 1
  • 18
  • 23
2

My logic behind is parseInt(res[0] + res[1], base) / Math.pow(base, res[1].length);

Ex : var fValue = '-1100.0011'; var result= parseInt('-11000011', base) / Math.pow(base, '0011'.length);

<script>
    parseFractionInt = (value, base = 2) => {
        var res = value.split('.');
        return res.length < 2 ? 
                parseInt(value, base) : 
                parseInt(res[0] + res[1], base) / Math.pow(base, res[1].length);
    }

    console.log(parseFractionInt('0.101'));  //0.625
    console.log(parseFractionInt('1100'));           //    12
    console.log(parseFractionInt('0.0011'));         //     0.1875
    console.log(parseFractionInt('1100.0011'));      //    12.1875

    console.log(parseFractionInt('ABC', 16));      //  2748
    console.log(parseFractionInt('0.DEF', 16));    //     0.870849609375
    console.log(parseFractionInt('ABC.DEF', 16));  //  2748.870849609375

    console.log(parseFractionInt('-ABC.DEF', 16)); // -2748.870849609375
    console.log(parseFractionInt('-1100.0011'));    //   -12.1875
    console.log(parseFractionInt('G.G', 16));   //NaN
    console.log(parseFractionInt('A.G', 16));   //0.625

</script>
Prabhat
  • 772
  • 1
  • 8
  • 22
  • 1
    How is this different than [Tomas Langkaas's answer](https://stackoverflow.com/a/58695018/3082296)? – adiga Nov 10 '19 at 10:55
  • Your using replace method and again & again your calling Math.pow() if there no fraction value also. It might more faster than your answer.. Thanks..!! – Prabhat Nov 10 '19 at 11:22