compress/urlencode a series of 100 base-4 numbers in javascript - javascript

First thing: must be done entirely in javascript. (JQuery/mootools optional)
I have a series of 100 numbers each set 0,1,2, or 3 - these represents settings on the page. I would like to encode these to the shortest string possible to create a permalink to the page.
I am thinking the best way would be to store them in binary couplets, convert those couplets to a string, ant then urlencode the string.
However the best I have found so far is parseint( binary_var, 2 ), which coverts a binary number to a base_10 number. However to get the string short enough I'll need a better system.
If I could convert to 64-bit encoding I could store all the data in just 4 chars, I think. I know urls support unicode now, and I believe I can use escape and unescape to encode/decode 64-bit chars, so the main thing I am looking for is a way to encode/decode binary data to 64-bit characters.
Of course I am not 100% sure this is the best way, or will even work, so it I am completely off track feel free to point me in the right direction.
Thanks!

You can encode such arrays of numbers into a string, 3 per character, like this:
function encodeBase4(base4) {
var i, rv = [], n = ~~((base4.length + 2) / 3) * 3;
for (i = 0; i < n; i += 3) {
rv.push(
32 +
((base4[i] || 0) & 3) +
((base4[i + 1] || 0) & 3) * 4 +
((base4[i + 2] || 0) & 3) * 16
);
}
return String.fromCharCode.apply(null, rv);
}
You can then convert the other direction like this:
function decodeBase4(str) {
var i, rv = [], n = str.length;
for (i = 0; i < n; ++i) {
var b = str.charCodeAt(i) - 32;
rv.push(b & 3);
rv.push(~~(b / 4) & 3);
rv.push(~~(b / 16) & 3);
}
return rv;
}
Here's the jsfiddle which seems to work on its simple test case. (Note that you end up with a list that's a multiple of 3 in length; you'd have to know how many real values there are and just ignore the zeros at the end.)
Now these result strings will be "dirty" and require URL encoding if you're putting them in URLs. If you packed only 2 numbers per character, you could make the resulting strings be all alphabetic, and thus you'd avoid the encoding penalty; however they'd be longer, of course.

100 pieces of information with 2 bits each require 200 bits in total. With base 64 encoding you would require ceil(200/log2(64)) = 34 characters.
A URI path segment allows 79 character that don’t require being encoded using the percent-encoding. If you add the path segment separator / you have 80 characters and thus require ceil(200/log2(80)) = 32 characters. That’s the optimum you can achieve using the path alone.
You could use more than these characters, even Unicode characters. But those would need to be encoded with the percent-encoding as URIs are only allowed to contain US-ASCII. A URI path like /ä (ä = U+00E4) is actually /%C3%A4 and only the browser displays it as /ä.
Here’s an example (functions taken from arbitrary base conversion in javascript):
function getValueOfDigit(digit, alphabet)
{
var pos = alphabet.indexOf(digit);
return pos;
}
function convert(src, srcAlphabet, dstAlphabet)
{
var srcBase = srcAlphabet.length;
var dstBase = dstAlphabet.length;
var wet = src;
var val = 0;
var mlt = 1;
while (wet.length > 0)
{
var digit = wet.charAt(wet.length - 1);
val += mlt * getValueOfDigit(digit, srcAlphabet);
wet = wet.substring(0, wet.length - 1);
mlt *= srcBase;
}
wet = val;
var ret = "";
while (wet >= dstBase)
{
var digitVal = wet % dstBase;
var digit = dstAlphabet.charAt(digitVal);
ret = digit + ret;
wet /= dstBase;
}
var digit = dstAlphabet.charAt(wet);
ret = digit + ret;
return ret;
}
var base4Alphabet = "0123",
base79Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$&'()*+,;=:#",
base80Alphabet = base79Alphabet+"/";
alert(convert(getValueOfDigit("010203210", base4Alphabet), base4Alphabet, base80Alphabet)); // "C#Q"

Related

encoding - Avoid repeating characters

I'd like to encode an Integer into a String using 4 different Bits ( A, B, C, D):
Therefore I've wrote a simple Int to customBase conversion you can find here:
function messageToCustomBase(message, charset) {
var base = charset.length,
integer = message,
result = ""
do {
var index = integer % base
result = charset[index] + result
integer = parseInt(integer/base)
} while (integer > 0)
return result
}
The code is working quite fine and encoding looks like this:
0 --> "A"
1 --> "B"
2 --> "C"
...
100 --> "BCBA"
...
10000 --> "CBDABAA"
But due to a special program, I am practically forced to use a special encode algorithm which converts the integer to a string which ...
1. should have as small a length as possible
2. uses maximum four different letters (4 different bits -> A, B, C, D)
3. Prevents never having the same letters next to each other
"ABADADA" -> legit
"ABCDAD" -> legit
"BABCA" -> legit
"CDDABC" -> not legit (because of "D" "D")
"BBBACAB" -> not legit (because of "B" "B" "B")
Question: > How can I avoid multiple characters following each other by editing my messageToBase function?
Example: Encoding Integer 42 will give the result "CCC".
Because in my special case "CCC" is not valid it should be encoded different (maybe to e.g. "CBCDCA", ...)
Note: The string must be able to be decoded later on so just adding random fill-characters between repeating characters after the decoding will not be a working solution.
Note: Here you can find a working fiddle for the base en/decoding
Edit: Theoretically we have to add in cases where it comes to repeating letters an additional bit ("E"). Otherwise it will just lead to complications with other Integers!
So I really got no clue how to fix this problem and any help would be very appreciated. :)
Here's a (rather brute-force) solution:
This simply iterates over your existing encoder with higher "bit depth" until it finds a solution without repeated letters. The encoding depth is prepended to the encoded value (otherwise it would be impossible to reverse the calculation, because you wouldn't have any way of knowing whether "BA" means 2 (encoded to depth B) or 3 (encoded to depth C).
This doesn't guarantee the lowest possible "bit depth", but it does keep the strings as short as possible given your encoding method.
Here's a demonstration of encoding and decoding numbers from 1 through 50:
var encode = function(number) {
if (number == 1) {
return "A-A"; // hacky workaround for endless loop in messageToCustomBase
}
var ret = "";
var bits = Math.floor(Math.log2(number) + 1); // minimum depth required for this number
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
for (var i = bits; i < 26; i++) {
var bitIdentifier = chars.charAt(i - 1);
var encoded = messageToCustomBase(number, chars.substr(0, i));
if (encoded.match(/(.)\1/)) {
// duplicate letters found, keep looking
} else {
return bitIdentifier + "-" + encoded
}
}
}
var decode = function(m) {
if (m === 'A-A') {
return 1
} // hack again
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var parts = m.split('-');
var charset = chars.substr(0, parts[0].charCodeAt(0) - 64);
return customBaseToMessage(parts[1], charset)
}
// exactly as your original code:
function messageToCustomBase(message, charset) {
var base = charset.length,
integer = message,
result = ""
do {
var index = integer % base
result = charset[index] + result
integer = parseInt(integer / base)
} while (integer > 0)
return result
}
function customBaseToMessage(message, charset) {
var base = charset.length,
result = 0
for (var i = 0; i < message.length; i++)  {
var index = charset.indexOf(message[i])
result = result * base + index
}
return result
}
// encode numbers for testing:
for (var i = 1; i < 51; i++) {
console.log(i, encode(i), decode(encode(i)));
}
You can do this by sticking to base 3 after the first loop, where the characters available are the three you didn't use for the previous character.
As a modification of your encoding code, this looks like
function messageToCustomBase(message, charset) {
var base = charset.length,
integer = message,
result = "",
previous_index = null
var index = integer % base
result = charset[index] + result
integer = parseInt(integer/base)
previous_index = index
while (integer > 0) {
var index = integer % (base - 1)
if (index >= previous_index) {
index++
}
result = charset[index] + result
integer = parseInt(integer/(base - 1))
previous_index = index
}
return result
}
This is pretty close to optimal, but not quite. The reason is that the index for the left-most character will never be zero. Basically, it's doing a "correct" conversion to base (3..4), and is refusing to have representations that start with 0. If you're willing to forgo that, you can get a bit shorter on average.

Make a utf-8 string shorter with a utf-32 encoding in Javascript?

I'm trying to find a way to compress/decompress a string in Javascript. By compress I mean to make the string look shorter (less char). That's my goal.
Here's an example of how things should work:
// The string that I want to make shorter
// It will only contain [a-zA-Z0-9] chars and some ponctuations like ()[]{}.,;'"!
var string = "I like bananas !";
// The compressed string, maybe something like "䐓㐛꯱字",
// which is shorter than the original
var shortString = compress(string);
// The original string, "I like banana !"
var originalString = decompress(shortString);
Here's my first idea (maybe there's a better way to get to my goal, and if so I'm interested in it).
I know that my original string will be in utf-8. So I'm thinking of using utf-32 for the encoding, which should divide by 4 the length of the string.
But I don't know how to do these 2 functions that construct new strings with different encoding. Here's the code I have so far that doesn't work...
function compress(string) {
string = unescape(encodeURIComponent(string));
var newString = '';
for (var i = 0; i < string.length; i++) {
var char = string.charCodeAt(i);
newString += parseInt(char, 8).toString(32);
}
return newString;
}
Since you're using a set of less than 100 characters and that javascript strings are encoded in UTF-16 (which mean you have 65536 possible characters), what you can do is concatenate the character codes so as to have one "compressed" character per two basic character. This allows you to compress strings to half the length.
Like this for example:
document.getElementById('compressBtn').addEventListener('click', function() {
var stringToCompress = document.getElementById('tocompress').value;
var compressedString = compress(stringToCompress);
var decompressedString = decompress(compressedString);
if (stringToCompress === decompressedString) {
document.getElementById('display').innerHTML = stringToCompress + ", length of " + stringToCompress.length  + " characters compressed to " + compressedString + ", length of " + compressedString.length + " characters back to " + decompressedString;
} else {
document.getElementById('display').innerHTML = "This string cannot be compressed"
}
})
function compress(string) {
string = unescape(encodeURIComponent(string));
var newString = '',
char, nextChar, combinedCharCode;
for (var i = 0; i < string.length; i += 2) {
char = string.charCodeAt(i);
if ((i + 1) < string.length) {
// You need to make sure that you don't have 3 digits second character else you might go over 65536.
// But in UTF-16 the 32 characters aren't in your basic character set. But it's a limitation, anything
// under charCode 32 will cause an error
nextChar = string.charCodeAt(i + 1) - 31;
// this is to pad the result, because you could have a code that is single digit, which would make
// decompression a bit harder
combinedCharCode = char + "" + nextChar.toLocaleString('en', {
minimumIntegerDigits: 2
});
// You take the concanated code string and convert it back to a number, then a character
newString += String.fromCharCode(parseInt(combinedCharCode, 10));
} else {
// Here because you won't always have pair number length
newString += string.charAt(i);
}
}
return newString;
}
function decompress(string) {
var newString = '',
char, codeStr, firstCharCode, lastCharCode;
for (var i = 0; i < string.length; i++) {
char = string.charCodeAt(i);
if (char > 132) {
codeStr = char.toString(10);
// You take the first part of the compressed char code, it's your first letter
firstCharCode = parseInt(codeStr.substring(0, codeStr.length - 2), 10);
// For the second one you need to add 31 back.
lastCharCode = parseInt(codeStr.substring(codeStr.length - 2, codeStr.length), 10) + 31;
// You put back the 2 characters you had originally
newString += String.fromCharCode(firstCharCode) + String.fromCharCode(lastCharCode);
} else {
newString += string.charAt(i);
}
}
return newString;
}
var stringToCompress = 'I like bananas!';
var compressedString = compress(stringToCompress);
var decompressedString = decompress(compressedString);
document.getElementById('display').innerHTML = stringToCompress + ", length of " + stringToCompress.length  + " characters compressed to " + compressedString + ", length of " + compressedString.length + " characters back to " + decompressedString;
body {
padding: 10px;
}
#tocompress {
width: 200px;
}
<input id="tocompress" placeholder="enter string to compress" />
<button id="compressBtn">
Compress input
</button>
<div id="display">
</div>
Regarding the possible use of UTF-32 to further compress, I'm not sure it's possible, I might be wrong on that, but from my understanding it's not feasible. Here's why:
The approach above is basically concatenating two 1 byte values in one 2 bytes value. This is possible because javascript strings are encoded in 2 bytes (or 16 bits) (note that from what I understand the engine could decide to store differently making this compression unnecessary from a purely memory space point of view - that being said, in the end, one character is considered being 16 bits). A cleaner way to make the compression above would in fact to user the binary numbers instead of the decimal, it would make much more sense. Like this for example:
document.getElementById('compressBtn').addEventListener('click', function() {
var stringToCompress = document.getElementById('tocompress').value;
var compressedString = compress(stringToCompress);
var decompressedString = decompress(compressedString);
if (stringToCompress === decompressedString) {
document.getElementById('display').innerHTML = stringToCompress + ", length of " + stringToCompress.length + " characters compressed to " + compressedString + ", length of " + compressedString.length + " characters back to " + decompressedString;
} else {
document.getElementById('display').innerHTML = "This string cannot be compressed"
}
})
function compress(string) {
string = unescape(encodeURIComponent(string));
var newString = '',
char, nextChar, combinedCharCode;
for (var i = 0; i < string.length; i += 2) {
// convert to binary instead of keeping the decimal
char = string.charCodeAt(i).toString(2);
if ((i + 1) < string.length) {
nextChar = string.charCodeAt(i + 1).toString(2) ;
// you still need padding, see this answer https://stackoverflow.com/questions/27641812/way-to-add-leading-zeroes-to-binary-string-in-javascript
combinedCharCode = "0000000".substr(char.length) + char + "" + "0000000".substr(nextChar.length) + nextChar;
// You take the concanated code string and convert it back to a binary number, then a character
newString += String.fromCharCode(parseInt(combinedCharCode, 2));
} else {
// Here because you won't always have pair number length
newString += string.charAt(i);
}
}
return newString;
}
function decompress(string) {
var newString = '',
char, codeStr, firstCharCode, lastCharCode;
for (var i = 0; i < string.length; i++) {
char = string.charCodeAt(i);
if (char > 132) {
codeStr = char.toString(2);
// You take the first part (the first byte) of the compressed char code, it's your first letter
firstCharCode = parseInt(codeStr.substring(0, codeStr.length - 7), 2);
// then the second byte
lastCharCode = parseInt(codeStr.substring(codeStr.length - 7, codeStr.length), 2);
// You put back the 2 characters you had originally
newString += String.fromCharCode(firstCharCode) + String.fromCharCode(lastCharCode);
} else {
newString += string.charAt(i);
}
}
return newString;
}
var stringToCompress = 'I like bananas!';
var compressedString = compress(stringToCompress);
var decompressedString = decompress(compressedString);
document.getElementById('display').innerHTML = stringToCompress + ", length of " + stringToCompress.length + " characters compressed to " + compressedString + ", length of " + compressedString.length + " characters back to " + decompressedString;
<input id="tocompress" placeholder="enter string to compress" />
<button id="compressBtn">
Compress input
</button>
<div id="display">
</div>
So why not push the logic and use utf-32, which should be 4 bytes, meaning four 1 byte characters. One problem is that javascript has 2 bytes string. It's true that you can use pairs of 16 bits characters to represent utf-32 characters. Like this:
document.getElementById('test').innerHTML = "\uD834\uDD1E";
<div id="test"></div>
But if you test the length of the resulting string, you'll see that it's 2, even if there's only one "character". So from a javascript perspective, you're not reducing the actual string length.
The other thing is that UTF-32 has in fact 221 characters. See here: https://en.wikipedia.org/wiki/UTF-32
It is a protocol to encode Unicode code points that uses exactly 32
bits per Unicode code point (but a number of leading bits must be zero
as there are fewer than 221 Unicode code points)
So you don't really have 4 bytes, in fact you don't even have 3, which would be needed to encode 3. So UTF-32 doesn't seem to be a way to compress even more. And since javascript has native 2 bytes strings, it seems to me to be the most efficient - using that approach at least.
If your strings only contain ASCII characters [0, 127] you can "compress" the string using a custom 6 or 7-bit code page.
You can do this several ways, but I think one of the simpler methods is to define an array holding all allowed characters - a LUT, lookup-table if you like, then use its index value as the encoded value. You would of course have to manually mask and shift the encoded value into a typed array.
If your LUT looked like this:
var lut = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,:;!(){}";
you would in this case deal with a LUT of length 71 which means we would need to use a 7-bit range or [0, 127] (if length were 64 we could've reduced the it to 6-bit [0, 63] values).
Then you would take each characters in the string and convert to index values (you would normally do all the following steps in a single operation but I have separated them for simplicity):
var lut = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,:;!(){}";
var str = "I like bananas !";
var page = [];
Array.prototype.forEach.call(str, function(ch) {
var i = lut.indexOf(ch);
if (i < 0) throw "Invalid character - can't encode";
page.push(i);
});
console.log("Intermediate page:", page);
You can always tweak the LUT so that the most used characters are in the beginning, then support variable encoding bit-range, find max value and use that to determine what range you want to encode in. You can add an initial bit as a flag as to which range the encoding uses (for example bit 0 set if 6-bit fits, otherwise use 7-bit range).
Now that you know the indices we can start to encode the binary output itself using a 7-bit approach. Since JavaScript only support byte values, i.e. 8-bit width, we have to do all the split, shift and merge operations manually.
This means we need to keep track of remainder and position on a bit-level.
Say first index value was the following 7-bit value (full 7-bit range for readability - all in pseudo format):
&b01111111
The first step would be to shift it over to bit position 0 and keep track of a remainder:
&b01111111 << 1
Resulting in:
&b11111110
^
new bit position: 7
new remainder : 1
Then the next index value, for example:
&b01010101
would be encoded like this - first convert to 7-bit value in its own byte representation:
&b01010101 << 1 => &b10101010
Then get the reminder part first. To obtain this will shift everything right-wise using 8-bit minus the current remainder (within modulo of 8):
remainderValue = &b10101010 >>> (8 - remainder)
leaving us with the following representation:
&b00000001
(Note that we use triple >>> to shift right to avoid issues with sign.)
Next step now is to merge this value with our previous value that has already been encoded and stored into our destination byte array - for this we'll use an OR operation:
Index 0 New value Result in index 0 (index of dst. array)
&b11111110 | &b00000001 => &b11111111
then go to next index in our destination array and store the rest of the current value, then update the remainder and position.
The "leftover" of the byte is calculated like this using the original (after shifting it) 7-bit byte value:
leftover = &b10101010 << remainder => &b01010100
which we now put into the next position:
Index 0 Index 1 (destination array index, not page index)
&b11111111 01010100
^
new bit position: 14
new remainder : 2
And so on with the remaining index values. See this answer for actual code on how you can do this in JavaScript - the code in this answer doesn't deal with string encoding per-se, but it shows how you can shift byte buffers bit-wise which is essentially the same you need for this task.
To calculate the remainder step, use 8-bits minus your custom bit-range:
step = 8 - newRange (here 7) => 1
This will also be the start remainder. For each character, you'll add the step to remainder after it has been processed, but remember to use modulo 8 (byte width) when you use it for shifting:
remainder += step;
numOfBitsToShift = remainder % 8;
Bit-position uses of course the bit-range, in this case 7:
bitPosition += 7;
Then to find which indices you're dealing with you divide the bitPosition on 8, if any decimal you have to deal with two indexes (old and new), if no decimal the current position represents new index only (only shift is needed for current index value).
You can also use modulo and when modulo of remainder = step you know you that you are dealing with a single index in the destination.
To calculate the final length you would use the bit-length and length of string, then ceil the result so that all characters will fit into a 8-byte byte array which is the only array we can get in JavaScript:
dstLength = Math.ceil(7 * str.length / 8);
To decode you just reverse all the steps.
An alternative, if you use long strings or have to move forward fast, is to use an established compressor such as zlib which has a very compact header as well as good performance in JavaScript in the case of the linked solution. This will also deal with "patterns" in the string to further optimize the resulting size.
Disclaimer: as this is mostly a theoretical answer there might be some errors. Feel free to comment if any are found. Refer to linked answer for actual code example.
for full code see here: https://repl.it/NyMl/1
using the Uint8Array you can work with the bytes.
let msg = "This is some message";
let data = []
for(let i = 0; i < msg.length; ++i){
data[i] = msg.charCodeAt(i);
}
let i8 = new Uint8Array(data);
let i16 = new Uint16Array(i8.buffer);
you could also think of a compression like this: http://pieroxy.net/blog/pages/lz-string/demo.html
if you don't want to use a 3rd party library, the lz based compression should be fairly simple. see here (wikipedia)
I use the same library mentioned above, lz-string https://github.com/pieroxy/lz-string, and it creates file sizes that are smaller than most of the binary formats like Protocol Buffers.
I compress via Node.js like this:
var compressedString = LZString.compressToUTF16(str);
And I decompress client side like this:
var decompressedString = LZString.decompressFromUTF16(str);

JavaScript - Convert 24 digit hexadecimal number to decimal, add 1 and then convert back?

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?
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"));
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.

How to prepend two chars at the beginning of an Int16Array?

For app-specific reasons I need to prepend exactly these two chars 'a,' (the a and one comma) at the beginning of an existing Int16Array
at the moment I tried with this code but it does not seem to work correctly:
function convertFloat32ToInt16(buffer) {
var prefix = 'a,',
prefixLength = prefix.length / 2, // divided by 2 because we deal with 16 bits, not 8 bits
bufferLength = buffer.length,
totalLength = prefixLength + bufferLength,
arr = new Int16Array(totalLength),
i
for (i = 0; i < prefixLength; i = i + 2) {
arr[i] = prefix.charCodeAt(i) + prefix.charCodeAt(i + 1)
}
for (i = prefixLength; i < totalLength; i++) {
arr[i] = Math.min(1, buffer[i - prefixLength]) * 0x7FFF // convert to 16 bit
}
return arr.buffer
}
Any suggestions how I can do it better and fix the above code?
Many thanks!
Why use an Int16Array if you need to store random characters in it? You're asking for problems doing this.
Why not just use a regular array? Replace your definition of arr with arr = [], and replace references to buffer with arr
In any case, you'll need to use a different data structure if you want to store random characters. You could always make your return line something like this:
return {buffer: arr.buffer, type: prefix}

How to convert decimal to hexadecimal in JavaScript

How do you convert decimal values to their hexadecimal equivalent in JavaScript?
Convert a number to a hexadecimal string with:
hexString = yourNumber.toString(16);
And reverse the process with:
yourNumber = parseInt(hexString, 16);
If you need to handle things like bit fields or 32-bit colors, then you need to deal with signed numbers. The JavaScript function toString(16) will return a negative hexadecimal number which is usually not what you want. This function does some crazy addition to make it a positive number.
function decimalToHexString(number)
{
if (number < 0)
{
number = 0xFFFFFFFF + number + 1;
}
return number.toString(16).toUpperCase();
}
console.log(decimalToHexString(27));
console.log(decimalToHexString(48.6));
The code below will convert the decimal value d to hexadecimal. It also allows you to add padding to the hexadecimal result. So 0 will become 00 by default.
function decimalToHex(d, padding) {
var hex = Number(d).toString(16);
padding = typeof (padding) === "undefined" || padding === null ? padding = 2 : padding;
while (hex.length < padding) {
hex = "0" + hex;
}
return hex;
}
function toHex(d) {
return ("0"+(Number(d).toString(16))).slice(-2).toUpperCase()
}
For completeness, if you want the two's-complement hexadecimal representation of a negative number, you can use the zero-fill-right shift >>> operator. For instance:
> (-1).toString(16)
"-1"
> ((-2)>>>0).toString(16)
"fffffffe"
There is however one limitation: JavaScript bitwise operators treat their operands as a sequence of 32 bits, that is, you get the 32-bits two's complement.
With padding:
function dec2hex(i) {
return (i+0x10000).toString(16).substr(-4).toUpperCase();
}
The accepted answer did not take into account single digit returned hexadecimal codes. This is easily adjusted by:
function numHex(s)
{
var a = s.toString(16);
if ((a.length % 2) > 0) {
a = "0" + a;
}
return a;
}
and
function strHex(s)
{
var a = "";
for (var i=0; i<s.length; i++) {
a = a + numHex(s.charCodeAt(i));
}
return a;
}
I believe the above answers have been posted numerous times by others in one form or another. I wrap these in a toHex() function like so:
function toHex(s)
{
var re = new RegExp(/^\s*(\+|-)?((\d+(\.\d+)?)|(\.\d+))\s*$/);
if (re.test(s)) {
return '#' + strHex( s.toString());
}
else {
return 'A' + strHex(s);
}
}
Note that the numeric regular expression came from 10+ Useful JavaScript Regular Expression Functions to improve your web applications efficiency.
Update: After testing this thing several times I found an error (double quotes in the RegExp), so I fixed that. HOWEVER! After quite a bit of testing and having read the post by almaz - I realized I could not get negative numbers to work.
Further - I did some reading up on this and since all JavaScript numbers are stored as 64 bit words no matter what - I tried modifying the numHex code to get the 64 bit word. But it turns out you can not do that. If you put "3.14159265" AS A NUMBER into a variable - all you will be able to get is the "3", because the fractional portion is only accessible by multiplying the number by ten(IE:10.0) repeatedly. Or to put that another way - the hexadecimal value of 0xF causes the floating point value to be translated into an integer before it is ANDed which removes everything behind the period. Rather than taking the value as a whole (i.e.: 3.14159265) and ANDing the floating point value against the 0xF value.
So the best thing to do, in this case, is to convert the 3.14159265 into a string and then just convert the string. Because of the above, it also makes it easy to convert negative numbers because the minus sign just becomes 0x26 on the front of the value.
So what I did was on determining that the variable contains a number - just convert it to a string and convert the string. This means to everyone that on the server side you will need to unhex the incoming string and then to determine the incoming information is numeric. You can do that easily by just adding a "#" to the front of numbers and "A" to the front of a character string coming back. See the toHex() function.
Have fun!
After another year and a lot of thinking, I decided that the "toHex" function (and I also have a "fromHex" function) really needed to be revamped. The whole question was "How can I do this more efficiently?" I decided that a to/from hexadecimal function should not care if something is a fractional part but at the same time it should ensure that fractional parts are included in the string.
So then the question became, "How do you know you are working with a hexadecimal string?". The answer is simple. Use the standard pre-string information that is already recognized around the world.
In other words - use "0x". So now my toHex function looks to see if that is already there and if it is - it just returns the string that was sent to it. Otherwise, it converts the string, number, whatever. Here is the revised toHex function:
/////////////////////////////////////////////////////////////////////////////
// toHex(). Convert an ASCII string to hexadecimal.
/////////////////////////////////////////////////////////////////////////////
toHex(s)
{
if (s.substr(0,2).toLowerCase() == "0x") {
return s;
}
var l = "0123456789ABCDEF";
var o = "";
if (typeof s != "string") {
s = s.toString();
}
for (var i=0; i<s.length; i++) {
var c = s.charCodeAt(i);
o = o + l.substr((c>>4),1) + l.substr((c & 0x0f),1);
}
return "0x" + o;
}
This is a very fast function that takes into account single digits, floating point numbers, and even checks to see if the person is sending a hex value over to be hexed again. It only uses four function calls and only two of those are in the loop. To un-hex the values you use:
/////////////////////////////////////////////////////////////////////////////
// fromHex(). Convert a hex string to ASCII text.
/////////////////////////////////////////////////////////////////////////////
fromHex(s)
{
var start = 0;
var o = "";
if (s.substr(0,2).toLowerCase() == "0x") {
start = 2;
}
if (typeof s != "string") {
s = s.toString();
}
for (var i=start; i<s.length; i+=2) {
var c = s.substr(i, 2);
o = o + String.fromCharCode(parseInt(c, 16));
}
return o;
}
Like the toHex() function, the fromHex() function first looks for the "0x" and then it translates the incoming information into a string if it isn't already a string. I don't know how it wouldn't be a string - but just in case - I check. The function then goes through, grabbing two characters and translating those in to ASCII characters. If you want it to translate Unicode, you will need to change the loop to going by four(4) characters at a time. But then you also need to ensure that the string is NOT divisible by four. If it is - then it is a standard hexadecimal string. (Remember the string has "0x" on the front of it.)
A simple test script to show that -3.14159265, when converted to a string, is still -3.14159265.
<?php
echo <<<EOD
<html>
<head><title>Test</title>
<script>
var a = -3.14159265;
alert( "A = " + a );
var b = a.toString();
alert( "B = " + b );
</script>
</head>
<body>
</body>
</html>
EOD;
?>
Because of how JavaScript works in respect to the toString() function, all of those problems can be eliminated which before were causing problems. Now all strings and numbers can be converted easily. Further, such things as objects will cause an error to be generated by JavaScript itself. I believe this is about as good as it gets. The only improvement left is for W3C to just include a toHex() and fromHex() function in JavaScript.
Without the loop:
function decimalToHex(d) {
var hex = Number(d).toString(16);
hex = "000000".substr(0, 6 - hex.length) + hex;
return hex;
}
// Or "#000000".substr(0, 7 - hex.length) + hex;
// Or whatever
// *Thanks to MSDN
Also isn't it better not to use loop tests that have to be evaluated?
For example, instead of:
for (var i = 0; i < hex.length; i++){}
have
for (var i = 0, var j = hex.length; i < j; i++){}
Combining some of these good ideas for an RGB-value-to-hexadecimal function (add the # elsewhere for HTML/CSS):
function rgb2hex(r,g,b) {
if (g !== undefined)
return Number(0x1000000 + r*0x10000 + g*0x100 + b).toString(16).substring(1);
else
return Number(0x1000000 + r[0]*0x10000 + r[1]*0x100 + r[2]).toString(16).substring(1);
}
Constrained/padded to a set number of characters:
function decimalToHex(decimal, chars) {
return (decimal + Math.pow(16, chars)).toString(16).slice(-chars).toUpperCase();
}
For anyone interested, here's a JSFiddle comparing most of the answers given to this question.
And here's the method I ended up going with:
function decToHex(dec) {
return (dec + Math.pow(16, 6)).toString(16).substr(-6)
}
Also, bear in mind that if you're looking to convert from decimal to hex for use in CSS as a color data type, you might instead prefer to extract the RGB values from the decimal and use rgb().
For example (JSFiddle):
let c = 4210330 // your color in decimal format
let rgb = [(c & 0xff0000) >> 16, (c & 0x00ff00) >> 8, (c & 0x0000ff)]
// Vanilla JS:
document.getElementById('some-element').style.color = 'rgb(' + rgb + ')'
// jQuery:
$('#some-element').css('color', 'rgb(' + rgb + ')')
This sets #some-element's CSS color property to rgb(64, 62, 154).
var number = 3200;
var hexString = number.toString(16);
The 16 is the radix and there are 16 values in a hexadecimal number :-)
function dec2hex(i)
{
var result = "0000";
if (i >= 0 && i <= 15) { result = "000" + i.toString(16); }
else if (i >= 16 && i <= 255) { result = "00" + i.toString(16); }
else if (i >= 256 && i <= 4095) { result = "0" + i.toString(16); }
else if (i >= 4096 && i <= 65535) { result = i.toString(16); }
return result
}
If you want to convert a number to a hexadecimal representation of an RGBA color value, I've found this to be the most useful combination of several tips from here:
function toHexString(n) {
if(n < 0) {
n = 0xFFFFFFFF + n + 1;
}
return "0x" + ("00000000" + n.toString(16).toUpperCase()).substr(-8);
}
AFAIK comment 57807 is wrong and should be something like:
var hex = Number(d).toString(16);
instead of
var hex = parseInt(d, 16);
function decimalToHex(d, padding) {
var hex = Number(d).toString(16);
padding = typeof (padding) === "undefined" || padding === null ? padding = 2 : padding;
while (hex.length < padding) {
hex = "0" + hex;
}
return hex;
}
And if the number is negative?
Here is my version.
function hexdec (hex_string) {
hex_string=((hex_string.charAt(1)!='X' && hex_string.charAt(1)!='x')?hex_string='0X'+hex_string : hex_string);
hex_string=(hex_string.charAt(2)<8 ? hex_string =hex_string-0x00000000 : hex_string=hex_string-0xFFFFFFFF-1);
return parseInt(hex_string, 10);
}
As the accepted answer states, the easiest way to convert from decimal to hexadecimal is var hex = dec.toString(16). However, you may prefer to add a string conversion, as it ensures that string representations like "12".toString(16) work correctly.
// Avoids a hard-to-track-down bug by returning `c` instead of `12`
(+"12").toString(16);
To reverse the process you may also use the solution below, as it is even shorter.
var dec = +("0x" + hex);
It seems to be slower in Google Chrome and Firefox, but is significantly faster in Opera. See http://jsperf.com/hex-to-dec.
I'm doing conversion to hex string in a pretty large loop, so I tried several techniques in order to find the fastest one. My requirements were to have a fixed-length string as a result, and encode negative values properly (-1 => ff..f).
Simple .toString(16) didn't work for me since I needed negative values to be properly encoded. The following code is the quickest I've tested so far on 1-2 byte values (note that symbols defines the number of output symbols you want to get, that is for 4-byte integer it should be equal to 8):
var hex = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
function getHexRepresentation(num, symbols) {
var result = '';
while (symbols--) {
result = hex[num & 0xF] + result;
num >>= 4;
}
return result;
}
It performs faster than .toString(16) on 1-2 byte numbers and slower on larger numbers (when symbols >= 6), but still should outperform methods that encode negative values properly.
Converting hex color numbers to hex color strings:
A simple solution with toString and ES6 padStart for converting hex color numbers to hex color strings.
const string = `#${color.toString(16).padStart(6, '0')}`;
For example:
0x000000 will become #000000
0xFFFFFF will become #FFFFFF
Check this example in a fiddle here
How to convert decimal to hexadecimal in JavaScript
I wasn't able to find a brutally clean/simple decimal to hexadecimal conversion that didn't involve a mess of functions and arrays ... so I had to make this for myself.
function DecToHex(decimal) { // Data (decimal)
length = -1; // Base string length
string = ''; // Source 'string'
characters = [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' ]; // character array
do { // Grab each nibble in reverse order because JavaScript has no unsigned left shift
string += characters[decimal & 0xF]; // Mask byte, get that character
++length; // Increment to length of string
} while (decimal >>>= 4); // For next character shift right 4 bits, or break on 0
decimal += 'x'; // Convert that 0 into a hex prefix string -> '0x'
do
decimal += string[length];
while (length--); // Flip string forwards, with the prefixed '0x'
return (decimal); // return (hexadecimal);
}
/* Original: */
D = 3678; // Data (decimal)
C = 0xF; // Check
A = D; // Accumulate
B = -1; // Base string length
S = ''; // Source 'string'
H = '0x'; // Destination 'string'
do {
++B;
A& = C;
switch(A) {
case 0xA: A='A'
break;
case 0xB: A='B'
break;
case 0xC: A='C'
break;
case 0xD: A='D'
break;
case 0xE: A='E'
break;
case 0xF: A='F'
break;
A = (A);
}
S += A;
D >>>= 0x04;
A = D;
} while(D)
do
H += S[B];
while (B--)
S = B = A = C = D; // Zero out variables
alert(H); // H: holds hexadecimal equivalent
You can do something like this in ECMAScript 6:
const toHex = num => (num).toString(16).toUpperCase();
If you are looking for converting Large integers i.e. Numbers greater than Number.MAX_SAFE_INTEGER -- 9007199254740991, then you can use the following code
const hugeNumber = "9007199254740991873839" // Make sure its in String
const hexOfHugeNumber = BigInt(hugeNumber).toString(16);
console.log(hexOfHugeNumber)
To sum it all up;
function toHex(i, pad) {
if (typeof(pad) === 'undefined' || pad === null) {
pad = 2;
}
var strToParse = i.toString(16);
while (strToParse.length < pad) {
strToParse = "0" + strToParse;
}
var finalVal = parseInt(strToParse, 16);
if ( finalVal < 0 ) {
finalVal = 0xFFFFFFFF + finalVal + 1;
}
return finalVal;
}
However, if you don't need to convert it back to an integer at the end (i.e. for colors), then just making sure the values aren't negative should suffice.
I haven't found a clear answer, without checks if it is negative or positive, that uses two's complement (negative numbers included). For that, I show my solution to one byte:
((0xFF + number +1) & 0x0FF).toString(16);
You can use this instruction to any number bytes, only you add FF in respective places. For example, to two bytes:
((0xFFFF + number +1) & 0x0FFFF).toString(16);
If you want cast an array integer to string hexadecimal:
s = "";
for(var i = 0; i < arrayNumber.length; ++i) {
s += ((0xFF + arrayNumber[i] +1) & 0x0FF).toString(16);
}
In case you're looking to convert to a 'full' JavaScript or CSS representation, you can use something like:
numToHex = function(num) {
var r=((0xff0000&num)>>16).toString(16),
g=((0x00ff00&num)>>8).toString(16),
b=(0x0000ff&num).toString(16);
if (r.length==1) { r = '0'+r; }
if (g.length==1) { g = '0'+g; }
if (b.length==1) { b = '0'+b; }
return '0x'+r+g+b; // ('#' instead of'0x' for CSS)
};
var dec = 5974678;
console.log( numToHex(dec) ); // 0x5b2a96
This is based on Prestaul and Tod's solutions. However, this is a generalisation that accounts for varying size of a variable (e.g. Parsing signed value from a microcontroller serial log).
function decimalToPaddedHexString(number, bitsize)
{
let byteCount = Math.ceil(bitsize/8);
let maxBinValue = Math.pow(2, bitsize)-1;
/* In node.js this function fails for bitsize above 32bits */
if (bitsize > 32)
throw "number above maximum value";
/* Conversion to unsigned form based on */
if (number < 0)
number = maxBinValue + number + 1;
return "0x"+(number >>> 0).toString(16).toUpperCase().padStart(byteCount*2, '0');
}
Test script:
for (let n = 0 ; n < 64 ; n++ ) {
let s=decimalToPaddedHexString(-1, n);
console.log(`decimalToPaddedHexString(-1,${(n+"").padStart(2)}) = ${s.padStart(10)} = ${("0b"+parseInt(s).toString(2)).padStart(34)}`);
}
Test results:
decimalToPaddedHexString(-1, 0) = 0x0 = 0b0
decimalToPaddedHexString(-1, 1) = 0x01 = 0b1
decimalToPaddedHexString(-1, 2) = 0x03 = 0b11
decimalToPaddedHexString(-1, 3) = 0x07 = 0b111
decimalToPaddedHexString(-1, 4) = 0x0F = 0b1111
decimalToPaddedHexString(-1, 5) = 0x1F = 0b11111
decimalToPaddedHexString(-1, 6) = 0x3F = 0b111111
decimalToPaddedHexString(-1, 7) = 0x7F = 0b1111111
decimalToPaddedHexString(-1, 8) = 0xFF = 0b11111111
decimalToPaddedHexString(-1, 9) = 0x01FF = 0b111111111
decimalToPaddedHexString(-1,10) = 0x03FF = 0b1111111111
decimalToPaddedHexString(-1,11) = 0x07FF = 0b11111111111
decimalToPaddedHexString(-1,12) = 0x0FFF = 0b111111111111
decimalToPaddedHexString(-1,13) = 0x1FFF = 0b1111111111111
decimalToPaddedHexString(-1,14) = 0x3FFF = 0b11111111111111
decimalToPaddedHexString(-1,15) = 0x7FFF = 0b111111111111111
decimalToPaddedHexString(-1,16) = 0xFFFF = 0b1111111111111111
decimalToPaddedHexString(-1,17) = 0x01FFFF = 0b11111111111111111
decimalToPaddedHexString(-1,18) = 0x03FFFF = 0b111111111111111111
decimalToPaddedHexString(-1,19) = 0x07FFFF = 0b1111111111111111111
decimalToPaddedHexString(-1,20) = 0x0FFFFF = 0b11111111111111111111
decimalToPaddedHexString(-1,21) = 0x1FFFFF = 0b111111111111111111111
decimalToPaddedHexString(-1,22) = 0x3FFFFF = 0b1111111111111111111111
decimalToPaddedHexString(-1,23) = 0x7FFFFF = 0b11111111111111111111111
decimalToPaddedHexString(-1,24) = 0xFFFFFF = 0b111111111111111111111111
decimalToPaddedHexString(-1,25) = 0x01FFFFFF = 0b1111111111111111111111111
decimalToPaddedHexString(-1,26) = 0x03FFFFFF = 0b11111111111111111111111111
decimalToPaddedHexString(-1,27) = 0x07FFFFFF = 0b111111111111111111111111111
decimalToPaddedHexString(-1,28) = 0x0FFFFFFF = 0b1111111111111111111111111111
decimalToPaddedHexString(-1,29) = 0x1FFFFFFF = 0b11111111111111111111111111111
decimalToPaddedHexString(-1,30) = 0x3FFFFFFF = 0b111111111111111111111111111111
decimalToPaddedHexString(-1,31) = 0x7FFFFFFF = 0b1111111111111111111111111111111
decimalToPaddedHexString(-1,32) = 0xFFFFFFFF = 0b11111111111111111111111111111111
Thrown: 'number above maximum value'
Note: Not too sure why it fails above 32 bitsize
rgb(255, 255, 255) // returns FFFFFF
rgb(255, 255, 300) // returns FFFFFF
rgb(0,0,0) // returns 000000
rgb(148, 0, 211) // returns 9400D3
function rgb(...values){
return values.reduce((acc, cur) => {
let val = cur >= 255 ? 'ff' : cur <= 0 ? '00' : Number(cur).toString(16);
return acc + (val.length === 1 ? '0'+val : val);
}, '').toUpperCase();
}
Arbitrary precision
This solution take on input decimal string, and return hex string. A decimal fractions are supported. Algorithm
split number to sign (s), integer part (i) and fractional part (f) e.g for -123.75 we have s=true, i=123, f=75
integer part to hex:
if i='0' stop
get modulo: m=i%16 (in arbitrary precision)
convert m to hex digit and put to result string
for next step calc integer part i=i/16 (in arbitrary precision)
fractional part
count fractional digits n
multiply k=f*16 (in arbitrary precision)
split k to right part with n digits and put them to f, and left part with rest of digits and put them to d
convert d to hex and add to result.
finish when number of result fractional digits is enough
// #param decStr - string with non-negative integer
// #param divisor - positive integer
function dec2HexArbitrary(decStr, fracDigits=0) {
// Helper: divide arbitrary precision number by js number
// #param decStr - string with non-negative integer
// #param divisor - positive integer
function arbDivision(decStr, divisor)
{
// algorithm https://www.geeksforgeeks.org/divide-large-number-represented-string/
let ans='';
let idx = 0;
let temp = +decStr[idx];
while (temp < divisor) temp = temp * 10 + +decStr[++idx];
while (decStr.length > idx) {
ans += (temp / divisor)|0 ;
temp = (temp % divisor) * 10 + +decStr[++idx];
}
if (ans.length == 0) return "0";
return ans;
}
// Helper: calc module of arbitrary precision number
// #param decStr - string with non-negative integer
// #param mod - positive integer
function arbMod(decStr, mod) {
// algorithm https://www.geeksforgeeks.org/how-to-compute-mod-of-a-big-number/
let res = 0;
for (let i = 0; i < decStr.length; i++)
res = (res * 10 + +decStr[i]) % mod;
return res;
}
// Helper: multiply arbitrary precision integer by js number
// #param decStr - string with non-negative integer
// #param mult - positive integer
function arbMultiply(decStr, mult) {
let r='';
let m=0;
for (let i = decStr.length-1; i >=0 ; i--) {
let n = m+mult*(+decStr[i]);
r= (i ? n%10 : n) + r
m= n/10|0;
}
return r;
}
// dec2hex algorithm starts here
let h= '0123456789abcdef'; // hex 'alphabet'
let m= decStr.match(/-?(.*?)\.(.*)?/) || decStr.match(/-?(.*)/); // separate sign,integer,ractional
let i= m[1].replace(/^0+/,'').replace(/^$/,'0'); // integer part (without sign and leading zeros)
let f= (m[2]||'0').replace(/0+$/,'').replace(/^$/,'0'); // fractional part (without last zeros)
let s= decStr[0]=='-'; // sign
let r=''; // result
if(i=='0') r='0';
while(i!='0') { // integer part
r=h[arbMod(i,16)]+r;
i=arbDivision(i,16);
}
if(fracDigits) r+=".";
let n = f.length;
for(let j=0; j<fracDigits; j++) { // frac part
let k= arbMultiply(f,16);
f = k.slice(-n);
let d= k.slice(0,k.length-n);
r+= d.length ? h[+d] : '0';
}
return (s?'-':'')+r;
}
// -----------
// TESTS
// -----------
let tests = [
["0",2],
["000",2],
["123",0],
["-123",0],
["00.000",2],
["255.75",5],
["-255.75",5],
["127.999",32],
];
console.log('Input Standard Abitrary');
tests.forEach(t=> {
let nonArb = (+t[0]).toString(16).padEnd(17,' ');
let arb = dec2HexArbitrary(t[0],t[1]);
console.log(t[0].padEnd(10,' '), nonArb, arb);
});
// Long Example (40 digits after dot)
let example = "123456789012345678901234567890.09876543210987654321"
console.log(`\nLong Example:`);
console.log('dec:',example);
console.log('hex: ',dec2HexArbitrary(example,40));
The problem basically how many padding zeros to expect.
If you expect string 01 and 11 from Number 1 and 17. it's better to use Buffer as a bridge, with which number is turn into bytes, and then the hex is just an output format of it. And the bytes organization is well controlled by Buffer functions, like writeUInt32BE, writeInt16LE, etc.
import { Buffer } from 'buffer';
function toHex(n) { // 4byte
const buff = Buffer.alloc(4);
buff.writeInt32BE(n);
return buff.toString('hex');
}
> toHex(1)
'00000001'
> toHex(17)
'00000011'
> toHex(-1)
'ffffffff'
> toHex(-1212)
'fffffb44'
> toHex(1212)
'000004bc'
Here's my solution:
hex = function(number) {
return '0x' + Math.abs(number).toString(16);
}
The question says: "How to convert decimal to hexadecimal in JavaScript". While, the question does not specify that the hexadecimal string should begin with a 0x prefix, anybody who writes code should know that 0x is added to hexadecimal codes to distinguish hexadecimal codes from programmatic identifiers and other numbers (1234 could be hexadecimal, decimal, or even octal).
Therefore, to correctly answer this question, for the purpose of script-writing, you must add the 0x prefix.
The Math.abs(N) function converts negatives to positives, and as a bonus, it doesn't look like somebody ran it through a wood-chipper.
The answer I wanted, would have had a field-width specifier, so we could for example show 8/16/32/64-bit values the way you would see them listed in a hexadecimal editing application. That, is the actual, correct answer.

Categories