JavaScript - Convert 24 digit hexadecimal number t

2019-05-23 13:50发布

For an ObjectId in MongoDB, I work with a 24 digit hexadecimal number. Because I need to keep track of a second collection, I need to add 1 to this hexadecimal number.

In my case, here's my value

var value = "55a98f19b27585d81922ba0b"

What I'm looking for is

var newValue = "55a98f19b25785d81922ba0c"

I tried to create a function for this

function hexPlusOne(hex) {
    var num = (("0x" + hex) / 1) + 1;
    return num.toString(16);
}

This works with smaller hex numbers

hexPlusOne("eeefab")
=> "eeefac"

but it fails miserably for my hash

hexPlusOne(value)
=> "55a98f19b275840000000000"

Is there a better way to solve this?

2条回答
Anthone
2楼-- · 2019-05-23 14:29

This version will return a string as long as the input string, so the overflow is ignored in case the input is something like "ffffffff".

function hexIncrement(str) {
    var hex = str.match(/[0-9a-f]/gi);
    var digit = hex.length;
    var carry = 1;

    while (digit-- && carry) {
        var dec = parseInt(hex[digit], 16) + carry;
        carry = Math.floor(dec / 16);
        dec %= 16;
        hex[digit] = dec.toString(16);
    }
    return(hex.join(""));
}

document.write(hexIncrement("55a98f19b27585d81922ba0b") + "<BR>");
document.write(hexIncrement("ffffffffffffffffffffffff"));

This version may return a string which is 1 character longer than the input string, because input like "ffffffff" carries over to become "100000000".

function hexIncrement(str) {
    var hex = str.match(/[0-9a-f]/gi);
    var digit = hex.length;
    var carry = 1;

    while (digit-- && carry) {
        var dec = parseInt(hex[digit], 16) + carry;
        carry = Math.floor(dec / 16);
        dec %= 16;
        hex[digit] = dec.toString(16);
    }
    if (carry) hex.unshift("1");
    return(hex.join(""));
}

document.write(hexIncrement("55a98f19b27585d81922ba0b") + "<BR>");
document.write(hexIncrement("ffffffffffffffffffffffff"));

I was curious te see whether user2864740's suggestion of working with 12-digit chunks would offer any advantage. To my surprise, even though the code looks more complicated, it's actually around twice as fast. But the first version runs 500,000 times per second too, so it's not like you're going to notice in the real world.

function hexIncrement(str) {
    var result = "";
    var carry = 1;
    while (str.length && carry) {
        var hex = str.slice(-12);
        if (/^f*$/i.test(hex)) {
            result = hex.replace(/f/gi, "0") + result;
            carry = 1;
        } else {
            result = ("00000000000" + (parseInt(hex, 16) + carry).toString(16)).slice(-hex.length) + result;
            carry = 0;
        }
        str = str.slice(0,-12);
    }
    return(str.toLowerCase() + (carry ? "1" : "") + result);
}

document.write(hexIncrement("55a98f19b27585d81922ba0b") + "<BR>");
document.write(hexIncrement("000000000000ffffffffffff") + "<BR>");
document.write(hexIncrement("0123456789abcdef000000000000ffffffffffff"));

查看更多
时光不老,我们不散
3楼-- · 2019-05-23 14:44

The error comes from attempting to covert the entire 24-digit hex value to a number first because it won't fit in the range of integers JavaScript can represent distinctly2. In doing such a conversion to a JavaScript number some accuracy is lost.

However, it can be processed as multiple (eg. two) parts: do the math on the right part and then the left part, if needed due to overflow1. (It could also be processed one digit at a time with the entire addition done manually.)

Each chunk can be 12 hex digits in size, which makes it an easy split-in-half.


1 That is, if the final num for the right part is larger than 0xffffffffffff, simply carry over (adding) one to the left part. If there is no overflow then the left part remains untouched.

2 See What is JavaScript's highest integer value that a Number can go to without losing precision?

The range is 2^53, but the incoming value is 16^24 ~ (2^4)^24 ~ 2^(4*24) ~ 2^96; still a valid number, but outside the range of integers that can be distinctly represented.


Also, use parseInt(str, 16) instead of using "0x" + str in a numeric context to force the conversion, as it makes the intent arguably more clear.

查看更多
登录 后发表回答