admin管理员组

文章数量:1427755

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?

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?

Share Improve this question asked Jul 18, 2015 at 0:10 Richard HamiltonRichard Hamilton 26.5k11 gold badges65 silver badges88 bronze badges 0
Add a ment  | 

2 Answers 2

Reset to default 6

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 bee "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 plicated, 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"));

The error es 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 ining 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.

本文标签: