24

I would like to convert a number in base 10 with fraction to a number in base 16.

var myno = 28.5;

var convno = myno.toString(16);
alert(convno);

All is well there. Now I want to convert it back to decimal.

But now I cannot write:

var orgno = parseInt(convno, 16);
alert(orgno);

As it doesn't return the decimal part.

And I cannot use parseFloat, since per MDC, the syntax of parseFloat is

parseFloat(str);

It wouldn't have been a problem if I had to convert back to int, since parseInt's syntax is

parseInt(str [, radix]);

So what is an alternative for this?

Disclaimer: I thought it was a trivial question, but googling didn't give me any answers.

This question made me ask the above question.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
naveen
  • 53,448
  • 46
  • 161
  • 251

9 Answers9

18

Another possibility is to parse the digits separately, splitting the string up in two and treating both parts as ints during the conversion and then add them back together.

function parseFloat(str, radix)
{
    var parts = str.split(".");
    if ( parts.length > 1 )
    {
        return parseInt(parts[0], radix) + parseInt(parts[1], radix) / Math.pow(radix, parts[1].length);
    }
    return parseInt(parts[0], radix);
}

var myno = 28.4382;
var convno = myno.toString(16);
var f = parseFloat(convno, 16);
console.log(myno + " -> " + convno + " -> " + f);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Kent
  • 568
  • 4
  • 12
  • thanks kent. i would rather call the function something other than parseFloat. but anyway its an overload :) – naveen Feb 20 '11 at 13:40
  • Don't forget to default the integer part to 0 if 'parts[0]' is the empty string. For example when parsing ".0f". See Kevin's answer below. – Jonas Sicking Jan 02 '17 at 17:44
6

Try this.

The string may be raw data (simple text) with four characters (0 - 255) or a hex string "0xFFFFFFFF" four bytes in length.

jsfiddle.net

var str = '0x3F160008';

function parseFloat(str) {
    var float = 0, sign, order, mantissa, exp,
    int = 0, multi = 1;
    if (/^0x/.exec(str)) {
        int = parseInt(str, 16);
    }
    else {
        for (var i = str.length -1; i >=0; i -= 1) {
            if (str.charCodeAt(i) > 255) {
                console.log('Wrong string parameter');
                return false;
            }
            int += str.charCodeAt(i) * multi;
            multi *= 256;
        }
    }
    sign = (int >>> 31) ? -1 : 1;
    exp = (int >>> 23 & 0xff) - 127;
    mantissa = ((int & 0x7fffff) + 0x800000).toString(2);
    for (i=0; i<mantissa.length; i+=1) {
        float += parseInt(mantissa[i]) ? Math.pow(2, exp) : 0;
        exp--;
    }
    return float*sign;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Asler
  • 79
  • 1
  • 3
5

Please try this:

function hex2dec(hex) {
    hex = hex.split(/\./);
    var len = hex[1].length;
    hex[1] = parseInt(hex[1], 16);
    hex[1] *= Math.pow(16, -len);
    return parseInt(hex[0], 16) + hex[1];
}

function hex2dec(hex) {
  hex = hex.split(/\./);
  var len = hex[1].length;
  hex[1] = parseInt(hex[1], 16);
  hex[1] *= Math.pow(16, -len);
  return parseInt(hex[0], 16) + hex[1];
}



// ----------
// TEST
// ----------

function calc(hex) {
  let dec = hex2dec(hex);
  msg.innerHTML = `dec: <b>${dec}</b><br>hex test: <b>${dec.toString(16)}</b>`
} 

let init="bad.a55";
inp.value=init;
calc(init);
<input oninput="calc(this.value)" id="inp" /><div id="msg"></div>
Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
Mark Eirich
  • 10,016
  • 2
  • 25
  • 27
4

I combined Mark's and Kent's answers to make an overloaded parseFloat function that takes an argument for the radix (much simpler and more versatile):

function parseFloat(string, radix)
{
    // Split the string at the decimal point
    string = string.split(/\./);
    
    // If there is nothing before the decimal point, make it 0
    if (string[0] == '') {
        string[0] = "0";
    }
    
    // If there was a decimal point & something after it
    if (string.length > 1 && string[1] != '') {
        var fractionLength = string[1].length;
        string[1] = parseInt(string[1], radix);
        string[1] *= Math.pow(radix, -fractionLength);
        return parseInt(string[0], radix) + string[1];
    }
    
    // If there wasn't a decimal point or there was but nothing was after it
    return parseInt(string[0], radix);
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
3

Try this:

  1. Decide how many digits of precision you need after the decimal point.
  2. Multiply your original number by that power of 16 (e.g. 256 if you want two digits).
  3. Convert it as an integer.
  4. Put the decimal point in manually according to what you decided in step 1.

Reverse the steps to convert back.

  1. Take out the decimal point, remembering where it was.
  2. Convert the hex to decimal in integer form.
  3. Divide the result by the the appropriate power of 16 (16^n, where n is the number of digits after the decimal point you took out in step 1).

A simple example:

Convert decimal 23.5 into hex, and want one digit after the decimal point after conversion.

23.5 x 16 = 376.

Converted to hex = 0x178.

Answer in base 16: 17.8

Now convert back to decimal:

Take out the decimal point: 0x178

Convert to decimal: 376

Divide by 16: 23.5

awm
  • 6,526
  • 25
  • 24
2

I'm not sure what hexadecimal format you wanted to parse there. Was this something like: "a1.2c"?

Floats are commonly stored in hexadecimal format using the IEEE 754 standard. That standard doesn't use any dots (which don't exist in pure hexadecimal alphabet). Instead of that there are three groups of bits of predefined length (1 + 8 + 23 = 32 bits in total ─ double uses 64 bits).

I've written the following function for parsing such a numbers into float:

function hex2float(num) {
    var sign = (num & 0x80000000) ? -1 : 1;
    var exponent = ((num >> 23) & 0xff) - 127;
    var mantissa = 1 + ((num & 0x7fffff) / 0x7fffff);
    return sign * mantissa * Math.pow(2, exponent);
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Zajec
  • 39
  • 1
  • 1
    In order to convert a number in base 10 you don't need to parse a hexadecimal format. Your did not answer the OP, but the 'reverse' question. – Anthon Jun 24 '12 at 10:37
  • It also doesn't work in javascript because the binary operators convert both their arguments to integers before performing the operation. So above you are operating on the integer part of 'num' rather than on the IEEE754 representation. Another reason it doesn't work is that JS uses 64-bit doubles rather than 32-bit floats. If you really wanted to operate on the IEEE754 representation you could use bytearrays to write a double/float and read back the underlying bytes. – Jonas Sicking Jan 02 '17 at 17:40
1

Here is a size-improvement of Mark Eirich's answer:

function hex2dec(hex) {
  let h = hex.split(/\./);
  return ('0x'+h[1])*(16**-h[1].length)+ +('0x'+h[0]);
}

function hex2dec(hex) {
  let h = hex.split(/\./);
  return ('0x'+h[1])*(16**-h[1].length)+ +('0x'+h[0]);
}

function calc(hex) {
  let dec = hex2dec(hex);
  msg.innerHTML = `dec: <b>${dec}</b><br>hex test: <b>${dec.toString(16)}</b>`
} 

let init = "bad.a55";
inp.value = init;
calc(init);
<input oninput="calc(this.value)" id="inp" /><div id="msg"></div>
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
1
private hexStringToFloat(hexString: string): number {
    return Buffer.from(hexString, 'hex').readFloatBE(0);
}
Tatoh
  • 11
  • 1
1

Someone might find this useful.

bytes to Float32

function Int2Float32(bytes) {
    var sign = (bytes & 0x80000000) ? -1 : 1;
    var exponent = ((bytes >> 23) & 0xFF) - 127;
    var significand = (bytes & ~(-1 << 23));

    if (exponent == 128)
        return sign * ((significand) ? Number.NaN : Number.POSITIVE_INFINITY);

    if (exponent == -127) {
        if (significand === 0) return sign * 0.0;
        exponent = -126;
        significand /= (1 << 22);
    } else significand = (significand | (1 << 23)) / (1 << 23);

    return sign * significand * Math.pow(2, exponent);
}
Harijs Krūtainis
  • 1,261
  • 14
  • 13