Why are items in the wrong cells in this Javascript array? - javascript

Somebody wrote this (very terrible) function to translate a numeric value from 0-999 to English words.
function getNumberWords(number) {
var list = new Array(1000);
list[000] = "zero";
list[001] = "one";
list[002] = "two";
list[003] = "three";
///skip a few
list[099] = "ninety nine";
list[100] = "one hundred";
list[101] = "one hundred and one";
///skip a few more
list[997] = "nine hundred and ninety seven";
list[998] = "nine hundred and ninety eight";
list[999] = "nine hundred and ninety nine";
return list[number];
}
There is some rather odd bug in here that I can't seem to figure out the cause of. Some, but not all of the elements are placed in the wrong cell.
I tried displaying the contentes of the list and it showed a pretty funky result:
> list.toString();
"zero,one,two,three,four,five,six,seven,ten,eleven,twelve,thirteen,fourteen,
fifteen,sixteen,seventeen,twenty,twenty one,twenty two,twenty three,twenty four,
twenty five,twenty six,twenty seven,thirty,thirty one,thirty two,thirty three,
thirty four,thirty five,thirty six,thirty seven,forty,forty one,forty two,"
///(skip a few)
"sixty six,sixty seven,seventy,seventy one,seventy two,seventy three,seventy four,
seventy five,seventy six,seventy seven,,,,,sixty eight,sixty nine,,,,,,,,,
seventy eight,seventy nine,eighty,eighty one,eighty two,eighty three,eighty four,"
///(and so on)
That is, elements 0-7 have the expected value. Elements 68, 69, and 78-999 also have the expected values. Elements 64-67 and 70-77 are empty. Elements 8-63 have incorrect values.
What in the world is going on here? Why are 15 cells empty, 56 cells incorrect, and the rest correct?

Numeric literals starting with 0 are interpreted as octal values (if they can be) — that is, numbers in base-8. This is the case in Javascript, C, C++, PHP, Perl, Bash and many other languages.
Now, base-8 22 is base-10 18 so you're not accessing the elements that you think you are. Your first eight array elements were fine because, naturally, base-8 0 is also base-10 0, and so on up to 7. A value like 069 did not cause confusion because it cannot represent anything in base-8, so Javascript falls back to base-10. Yuck!
I suggest using spacing for alignment instead:
function getNumberWords(number) {
var list = new Array(1000);
list[ 0] = "zero";
list[ 1] = "one";
list[ 2] = "two";
list[ 3] = "three";
///skip a few
list[ 99] = "ninety nine";
list[100] = "one hundred";
list[101] = "one hundred and one";
///skip a few more
list[997] = "nine hundred and ninety seven";
list[998] = "nine hundred and ninety eight";
list[999] = "nine hundred and ninety nine";
return list[number];
}
I also suggest making a new function that generates the strings on-the-fly; it shouldn't be taxing and certainly no more so than creating this array on every call.

In Javascript 022 means 22 in octal (18 in decimal).
Octal numeral system on Wikipedia

Starting a number literal with a 0 in non-strict code may cause that number to be parsed as an Octal literal (base 8). For instance, 010 is parsed to a number with the value 8.
Octal literals are now deprecated and will not be parsed as octals in strict mode, using "use strict":
function a() {
alert(010);
// -> 8
}
function b() {
"use strict";
alert(010);
// -> 10
}
Not all browser support strict mode, so for now just change your code to make sure numbers do not start with a 0, or wrap them in strings:
list[ 21 ] = "something"
list["022"] = "something else";
Strings work because octal numbers are not coerced.

The 0-prefix means octal in JavaScript, so 022 is octal:
022 = 2*8^1+2*8^0 = 18
And since the first index i 0, 18 gives the 19th element.

In JavaScript, numbers beginning with 0 are interpreted as octal (base 8).
For example, 010 === 8.
Oddly, JavaScript will interpret the number as decimal (base 10) even if it has an octal prefix, if the number is impossible to reach in octal.
For example, 08 === 8.

Related

How does bitwise AND OR and XOR works on -negative signed integers?

I was just solving random problems on bitwise operators and trying various other combination for making personal notes. And somehow I just cannot figure out the solution.
Say I wanted to check bitwise AND between two integers or on a ~number and -negative number(~num1 & -num2) and various other combo's. Then I can see the answer but I haven't been able to establish how this happened?
Console:
console.log(25 & 3); outputs 1 (I can solve this easily).
console.log(-25 & -3); outputs-27.
Similarly
console.log(~25 & ~3); outputs -28.
console.log(25 & ~3); outputs -24.
console.log(~25 & 3); outputs -2.
console.log(~25 & -3); outputs --28.
console.log(-25 & ~3); outputs --28.
I know the logic behind "console.log(25 & -3)".
25 is 11001
-3 is 11101(3=00011 The minus sign is like 2s compliment+1)
AND-11001 = 25.
But I cannot make it work the same way when both the numbers are negative or with the other cases mentioned above. I have tried various combinations of numbers too, not just these two. But I cannot solve the problem. Can somebody explain the binary logic used in the problems I cannot solve.
(I've spend about 2 hrs here on SO to find the answer and another 1 hr+ on google, but I still haven't found the answer).
Thanks and Regards.
JavaScript specifies that bitwise operations on integers are performed as though they were stored in two's-complement notation. Fortunately, most computer hardware nowadays uses this notation natively anyway.
For brevity's sake I'm going to show the following numbers as 8-bit binary. They're actually 32-bit in JavaScript, but for the numbers in the original question, this doesn't change the outcome. It does, however, let us drop a whole lot of leading bits.
console.log(-25 & -3); //outputs -27. How?
If we write the integers in binary, we get (11100111 & 11111101) respectively. AND those together and you get 11100101, which is -27.
In your later examples, you seem to be using the NOT operator (~) and negation (-) interchangeably. You can't do that in two's complement: ~ and - are not the same thing. ~25 is 11100110, which is -26, not -25. Similarly, ~3 is 11111100, which is -4, not -3.
But when we put these together, we can work out the examples you gave.
console.log(~25 & ~3); //outputs-28. How?
11100110 & 11111100 = 11100100, which is -28 (not 28, as you wrote)
console.log(25 & ~3);//outputs-24. How?
00011001 & 11111100 = 00011000, which is 24
console.log(~25 & 3);//outputs-2. How?
11100110 & 00000011 = 00000001, which is 2
console.log(~25 & -3);//outputs--28. How?
11100110 & 11111101 = 11100100, which is -28
console.log(-25 & ~3);//outputs--28. How?
11100111 & 11111100 = 11100100, which is -28
The real key to understanding this is that you don't really use bitwise operations on integers. You use them on bags of bits of a certain size, and these bags of bits happen to be conveniently representable as integers. This is key to understanding what's going on here, because you've stumbled across a case where the difference matters.
There are specific circumstances in computer science where you can manipulate bags of bits in ways that, by coincidence, give the same results as if you'd done particular mathematical operations on numbers. But this only works in specific circumstances, and they require you to assume certain things about the numbers you're working on, and if your numbers don't fit those assumptions, things break down.
This is one of the reasons Donald Knuth said "premature optimization is the root of all evil". If you want to use bitwise operations in place of actual integer math, you have to be absolutely certain that your inputs will actually follow the assumptions required for that trick to work. Otherwise, the results will start looking strange when you start using inputs outside of those assumptions.
25 = 16+8+1 = 0b011001, I've added another 0 digit as the sign digit. Practically you'll have at least 8 binary digits
but the two's complement math is the same. To get -25 in 6-bits two's complement, you'd do -25 = ~25 + 1=0b100111
3=2+1=0b000011; -3 = ~3+1 = 0b111101
When you & the two, you get:
-25 = ~25 + 1=0b100111
&
-3 = ~3 + 1 = 0b111101
0b100101
The leftmost bit (sign bit) is set so it's a negative number. To find what it's a negative of, you reverse the process and first subtract 1 and then do ~.
~(0b100101-1) = 0b011011
thats 1+2+0*4+8+16 = 27 so -25&-3=-27.
For 25 & ~3, it's:
25 = 16+8+1 = 0b011001
& ~3 = 0b111100
______________________
0b011000 = 24
For ~25 & 3, it's:
~25 = 0b100110
& ~3 = 0b000011
______________________
0b000010 = 2
For ~25 & -3, it's:
~25 = 0b100110
& ~3+1 = 0b111101
______________________
0b100100 #negative
#find what it's a negative of:
~(0b100100-1) =~0b100011 = 0b011100 = 4+8+16 = 28
0b100100 = -28
-27 has 6 binary digits in it so you should be using numbers with at least that many digits. With 8-bit numbers then we have:
00011001 = 25
00000011 = 3
00011011 = 27
and:
11100111 = -25
11111101 = -3
11100101 = -27
Now -25 & -3 = -27 because 11100111 & 11111101 = 11100101
The binary string representation of a 32 bit integer can be found with:
(i >>> 0).toString(2).padStart(32, '0')
The bitwise anding of two binary strings is straightforward
The integer value of a signed, 32 bit binary string is either
parseInt(bitwiseAndString, 2)
if the string starts with a '0', or
-~parseInt(bitwiseAndString, 2) - 1
if it starts with a '1'
Putting all that together:
const tests = [
['-25', '-3'],
['~25', '-3'],
['25', '~3'],
['~25', '3'],
['~25', '~3'],
['-25', '~3']
]
const output = (s,t) => { console.log(`${`${s}:`.padEnd(20, ' ')}${t}`); }
const bitwiseAnd = (i, j) => {
console.log(`Calculating ${i} & ${j}`);
const bitStringI = (eval(i) >>> 0).toString(2).padStart(32, '0');
const bitStringJ = (eval(j) >>> 0).toString(2).padStart(32, '0');
output(`bit string for ${i}`, bitStringI);
output(`bit string for ${j}`, bitStringJ);
const bitArrayI = bitStringI.split('');
const bitArrayJ = bitStringJ.split('');
const bitwiseAndString = bitArrayI.map((s, idx) => s === '1' && bitArrayJ[idx] === '1' ? '1' : '0').join('');
output('bitwise and string', bitwiseAndString);
const intValue = bitwiseAndString[0] === '1' ? -~parseInt(bitwiseAndString, 2) - 1 : parseInt(bitwiseAndString, 2);
if (intValue === (eval(i) & eval(j))) {
console.log(`integer value: ${intValue} ✓`);
} else {
console.error(`calculation failed: ${intValue} !== ${i & j}`);
}
}
tests.forEach(([i, j]) => { bitwiseAnd(i, j); })

Generate big numbers with Math random in Javascript

I need to generate 26 digit numbers with Math.random, but when I use this:
Math.floor(Math.random() * 100000000000000000000000000) + 900000000000000000000000000
I gets 9.544695043285823e+26
Modern browsers support BigInt and bigint primitive type and we can combine it with a random generated array containing 8 bytes (the sizeof bigint is 8 bytes (64 bits)).
1. Generating Random BigInt Performance Wise
We can generate a random hex string of 16 characters length and apply it directly to BigInt:
const hexString = Array(16)
.fill()
.map(() => Math.round(Math.random() * 0xF).toString(16))
.join('');
const randomBigInt = BigInt(`0x${hexString}`);
// randomBigInt will contain a random Bigint
document.querySelector('#generate').addEventListener('click', () => {
const output = [];
let lines = 10;
do {
const hexString = Array(16)
.fill()
.map(() => Math.round(Math.random() * 0xF).toString(16))
.join('');
const number = BigInt(`0x${hexString}`);
output.push(`${
number.toString().padStart(24)
} : 0x${
hexString.padStart(16, '0')
}`);
} while (--lines > 0);
document.querySelector('#numbers').textContent = output.join('\n');
});
<button id="generate">Generate</button>
<pre id="numbers"><pre>
2. Generating Random BigInt from random bytes array
If we want to use Uint8Array or if we want more control over the bits manipulation, we can combine Array.prototype.fill with Array.prototype.map to generate an array containing 8 random byte number values (beware this is around 50% slower than the above method):
const randomBytes = Array(8)
.fill()
.map(() => Math.round(Math.random() * 0xFF));
// randomBytes will contain something similar to this:
// [129, 59, 98, 222, 20, 7, 196, 244]
Then we use Array.prototype.reduce to initialize a BigInt of zero value and left shift each randum byte value its position X 8 bits and applying bitwise or to the current value of each reduce iteration:
const randomBigInt = randomBytes
.reduce((n, c, i) => n | BigInt(c) << BigInt(i) * 8n, 0n);
// randomBigInt will contain a random Bigint
Working example generating 10 random BigInt values
document.querySelector('#generate').addEventListener('click', () => {
const output = [];
let lines = 10;
do {
const number = Array(8)
.fill()
.map(() => Math.round(Math.random() * 0xFF))
.reduce((n, c, i) => n | BigInt(c) << BigInt(i) * 8n, 0n);
output.push(`${
number.toString().padStart(24)
} : 0x${
number.toString(16).padStart(16, '0')
}`);
} while (--lines > 0);
document.querySelector('#numbers').textContent = output.join('\n');
});
<button id="generate">Generate</button>
<pre id="numbers"><pre>
Floating point numbers in JavaScript (and a lot of other languages) can contain only about 15.955 digits without losing precision. For bigger numbers you can look into JS libraries, or concatenate few numbers as strings. For example:
console.log( Math.random().toString().slice(2, 15) + Math.random().toString().slice(2, 15) )
Your question seems to be an XY problem where what you really want to do is generate a sequence of 26 random digits. You don't necessarily have to use Math.random. Whenever one mentions randomness it's important to specify how that randomness is distributed, otherwise you could end up with a paradox.
I'll assume you want each of the 26 digits to be independently randomly chosen uniformly from each of the 10 digits from 0 to 9, but I can also see a common interpretation being that the first digit must not be 0, and so that digit would be chosen uniformly from numbers 1 to 9.
Other answers may tempt you to choose a random bigint value using what amounts to random bits, but their digits will not be randomly distributed in the same way, since their maximum value is not a power of 10. For a simple example consider that a random 4 bit binary value in decimal will range from 00 to 15, and so the second digit will have a 12/16(=75%) chance of being 0 to 5, though it should be 60%.
As for an implementation, there's many ways to go about it. The simplest way would be to append to a string 26 times, but there are more potentially efficient ways that you could investigate for yourself if you find the performance isn't adequate. Math.random has a roughly uniform distribution from 0 to 1, but by being double precision it only has 15 or so significant decimal digits to offer us, so for each call to Math.random we should be able to retrieve up to 15 out of 26 decimal digits. Using this fact, I would suggest the following compromise on ease of readability and efficiency:
function generate26Digits() {
const first13 = Math.floor(Math.random() * Math.pow(10, 13)).toFixed(0).padStart(13, "0");
const next13 = Math.floor(Math.random() * Math.pow(10, 13)).toFixed(0).padStart(13, "0");
return first13 + next13;
}
console.log(generate26Digits())
This solution is not cryptographically secure however, and so I will direct readers to use Crypto.getRandomValues if you need more security for this for some reason.
If you then want to do math on this number as well without losing precision, you will have to use bigint types as others have suggested.
If I use a 6 decillion LG about 72.9 million out of 300 but when I switch to 10 centillion it comes like this 10 ^ 999 - 99999 + 26 just like how 9 - 9 - 9 - 9 - googolplex is equal to 0 googolplex

Compressing a Hex String in JavaScript/NodeJS

My app generates links, which contain hex string like: 37c1fbcabbc31f2f8d2ad31ceb91cd8d0d189ca5963dc6d353188d3d5e75b8b3e401d4e74e9b3e02efbff0792cda5c4620cb3b1f84aeb47b8d2225cd40e761a5. I would really like to make them shorter, like the solution mentioned for Ruby in Compressing a hex string in Ruby/Rails.
Is there a way to do this in JavaScript/NodeJS?
node int-encoder does this, using the strategy already mentioned.
it also supports large numbers
npm install int-encoder
var en = require('int-encoder');
//simple integer conversion
en.encode(12345678); // "ZXP0"
en.decode('ZXP0'); // 12345678
//convert big hex number using optional base argument
en.encode('e6c6b53d3c8160b22dad35a0f705ec09', 16); // 'hbDcW9aE89tzLYjDgyzajJ'
en.decode('hbDcW9aE89tzLYjDgyzajJ', 16); // 'e6c6b53d3c8160b22dad35a0f705ec09'
You could use toString and parseInt method, that basically are doing the same thing of the methods you mentioned in the link:
var hexString = "4b3fc1400";
var b36 = parseInt(hexString, 16).toString(36); // "9a29mgw"
And to convert it back, you just need to do the opposite:
hexString = parseInt(b36, 36).toString(16); // "4b3fc1400"
The only problem with your string, is that is too big to be threat as number in JavaScript. You should split them in chunk. JavaScript's numbers are accurate up to 2^53 (plus sign), so the max positive number you can handle is 0x20000000000000 (in hexadecimal, that is 9007199254740992 in decimal); you can use the accuracy to handle the chunk:
var hexString = "37c1fbcabbc31f2f8d2ad31ceb91cd8d0d189ca5963dc6d353188d3d5e75b8b3e401d4e74e9b3e02efbff0792cda5c4620cb3b1f84aeb47b8d2225cd40e761a5"
var b36 = "", b16 = "";
var chunk, intChunk;
// 14 is the length of 0x20000000000000 (2^53 in base 16)
for (var i = 0, max = 14; i < hexString.length; i += max) {
chunk = hexString.substr(i, max);
intChunk = parseInt(chunk, 16);
if (intChunk.toString(16) !== chunk) {
intChunk = parseInt(hexString.substr(i, max - 1), 16);
i -= 1;
}
b36 += intChunk.toString(36)
}
// 11 is the length of 2gosa7pa2gv (2^53 in base 36)
for (var i = 0, max = 11; i < b36.length; i += max ) {
chunk = b36.substr(i, max);
intChunk = parseInt(chunk, 36);
if (intChunk.toString(36) !== chunk) {
intChunk = parseInt(b36.substr(i, max - 1), 36);
i -= 1;
}
b16 += intChunk.toString(16)
}
console.log(hexString);
console.log(b36);
console.log(b16);
Update: You could also use a base 62 instead of 36 to compress more, but notice that JS supports up to base 36, so you need to implement that personal notation manually (I believe there are already some implementation around).
The simplest and fastest thing to do is define a set of 64 safe characters for use in the URL, such as A-Z, a-z, 0-9, _, and $. Then encode every three hex digits (4 bits each) into two safe characters (6 bits each). This requires no multiplication and division, and it can be used on arbitrarily long strings.
You will need to pick a 65th character to use at the end of the string to indicate if the last four-bit piece is used or not. Otherwise you will have an ambiguity for character strings with an even number of characters. Let's call it 2n. Then there are either 3n-1 or 3n hex digits encoded within, but there is no way to tell which. You can follow the sequence with a special character to indicate one of those cases. E.g. a '.' (period).
Note: The last few characters picked here for the set differ from Base64 encoding, since URLs have their own definition of safe punctuation characters. See RFC 1738.

Bitwise XOR in Javascript compared to C++

I am porting a simple C++ function to Javascript, but it seems I'm running into problems with the way Javascript handles bitwise operators.
In C++:
AnsiString MyClass::Obfuscate(AnsiString source)
{
int sourcelength=source.Length();
for(int i=1;i<=sourcelength;i++)
{
source[i] = source[i] ^ 0xFFF;
}
return source;
}
Obfuscate("test") yields temporary intvalues
-117, -102, -116, -117
Obfuscate ("test") yields stringvalue
‹šŒ‹
In Javascript:
function obfuscate(str)
{
var obfuscated= "";
for (i=0; i<str.length;i++) {
var a = str.charCodeAt(i);
var b = a ^ 0xFFF;
obfuscated= obfuscated+String.fromCharCode(b);
}
return obfuscated;
}
obfuscate("test") yields temporary intvalues
3979 , 3994 , 3980 , 3979
obfuscate("test") yields stringvalue
ྋྚྌྋ
Now, I realize that there are a ton of threads where they point out that Javascript treats all numbers as floats, and bitwise operations involve a temporary cast to 32bit int.
It really wouldn't be a problem except for that I'm obfuscating in Javascript and reversing in C++, and the different results don't really match.
How do i tranform the Javascript result into the C++ result? Is there some simple shift available?
Working demo
Judging from the result that xoring 116 with 0xFFF gives -117, we have to emulate
2's complement 8-bit integers in javascript:
function obfuscate(str)
{
var bytes = [];
for (var i=0; i<str.length;i++) {
bytes.push( ( ( ( str.charCodeAt(i) ^ 0xFFF ) & 0xFF ) ^ 0x80 ) -0x80 );
}
return bytes;
}
Ok these bytes are interpreted in windows cp 1252 and if they are negative, probably just subtracted from 256.
var ascii = [
0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000A,0x000B,0x000C,0x000D,0x000E,0x000F
,0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001A,0x001B,0x001C,0x001D,0x001E,0x001F
,0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002A,0x002B,0x002C,0x002D,0x002E,0x002F
,0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003A,0x003B,0x003C,0x003D,0x003E,0x003F
,0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004A,0x004B,0x004C,0x004D,0x004E,0x004F
,0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005A,0x005B,0x005C,0x005D,0x005E,0x005F
,0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006A,0x006B,0x006C,0x006D,0x006E,0x006F
,0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007A,0x007B,0x007C,0x007D,0x007E,0x007F
];
var cp1252 = ascii.concat([
0x20AC,0xFFFD,0x201A,0x0192,0x201E,0x2026,0x2020,0x2021,0x02C6,0x2030,0x0160,0x2039,0x0152,0xFFFD,0x017D,0xFFFD
,0xFFFD,0x2018,0x2019,0x201C,0x201D,0x2022,0x2013,0x2014,0x02DC,0x2122,0x0161,0x203A,0x0153,0xFFFD,0x017E,0x0178
,0x00A0,0x00A1,0x00A2,0x00A3,0x00A4,0x00A5,0x00A6,0x00A7,0x00A8,0x00A9,0x00AA,0x00AB,0x00AC,0x00AD,0x00AE,0x00AF
,0x00B0,0x00B1,0x00B2,0x00B3,0x00B4,0x00B5,0x00B6,0x00B7,0x00B8,0x00B9,0x00BA,0x00BB,0x00BC,0x00BD,0x00BE,0x00BF
,0x00C0,0x00C1,0x00C2,0x00C3,0x00C4,0x00C5,0x00C6,0x00C7,0x00C8,0x00C9,0x00CA,0x00CB,0x00CC,0x00CD,0x00CE,0x00CF
,0x00D0,0x00D1,0x00D2,0x00D3,0x00D4,0x00D5,0x00D6,0x00D7,0x00D8,0x00D9,0x00DA,0x00DB,0x00DC,0x00DD,0x00DE,0x00DF
,0x00E0,0x00E1,0x00E2,0x00E3,0x00E4,0x00E5,0x00E6,0x00E7,0x00E8,0x00E9,0x00EA,0x00EB,0x00EC,0x00ED,0x00EE,0x00EF
,0x00F0,0x00F1,0x00F2,0x00F3,0x00F4,0x00F5,0x00F6,0x00F7,0x00F8,0x00F9,0x00FA,0x00FB,0x00FC,0x00FD,0x00FE,0x00FF
]);
function toStringCp1252(bytes){
var byte, codePoint, codePoints = [];
for( var i = 0; i < bytes.length; ++i ) {
byte = bytes[i];
if( byte < 0 ) {
byte = 256 + byte;
}
codePoint = cp1252[byte];
codePoints.push( codePoint );
}
return String.fromCharCode.apply( String, codePoints );
}
Result
toStringCp1252(obfuscate("test"))
//"‹šŒ‹"
I'm guessing that AnsiString contains 8-bit characters (since the ANSI character set is 8 bits). When you assign the result of the XOR back to the string, it is truncated to 8 bits, and so the resulting value is in the range [-128...127].
(On some platforms, it could be [0..255], and on others the range could be wider, since it's not specified whether char is signed or unsigned, or whether it's 8 bits or larger).
Javascript strings contain unicode characters, which can hold a much wider range of values, the result is not truncated to 8 bits. The result of the XOR will have a range of at least 12 bits, [0...4095], hence the large numbers you see there.
Assuming the original string contains only 8-bit characters, then changing the operation to a ^ 0xff should give the same results in both languages.
I assume that AnsiString is in some form, an array of chars. And this is the problem. in c, char can typically only hold 8-bits. So when you XOR with 0xfff, and store the result in a char, it is the same as XORing with 0xff.
This is not the case with javascript. JavaScript using Unicode. This is demonstrated by looking at the integer values:
-117 == 0x8b and 3979 == 0xf8b
I would recommend XORing with 0xff as this will work in both languages. Or you can switch your c++ code to use Unicode.
First, convert your AnsiString to wchar_t*. Only then obfuscate its individual characters:
AnsiString MyClass::Obfuscate(AnsiString source)
{
/// allocate string
int num_wchars = source.WideCharBufSize();
wchar_t* UnicodeString = new wchar_t[num_wchars];
source.WideChar(UnicodeString, source.WideCharBufSize());
/// obfuscate individual characters
int sourcelength=source.Length();
for(int i = 0 ; i < num_wchars ; i++)
{
UnicodeString[i] = UnicodeString[i] ^ 0xFFF;
}
/// create obfuscated AnsiString
AnsiString result = AnsiString(UnicodeString);
/// delete tmp string
delete [] UnicodeString;
return result;
}
Sorry, I'm not an expert on C++ Builder, but my point is simple: in JavaScript you have WCS2 symbols (or UTF-16), so you have to convert AnsiString to wide chars first.
Try using WideString instead of AnsiString
I don't know AnsiString at all, but my guess is this relates to the width of its characters. Specifically, I suspect they're less than 32 bits wide, and of course in bitwise operations, the width of what you're operating on matters, particularly when dealing with 2's complement numbers.
In JavaScript, your "t" in "test" is character code 116, which is b00000000000000000000000001110100. 0xFFF (4095) is b00000000000000000000111111111111, and the result you're getting (3979) is b00000000000000000000111110001011. We can readily see that you're getting the right result for the XOR:
116 = 00000000000000000000000001110100
4095 = 00000000000000000000111111111111
3979 = 00000000000000000000111110001011
So I'm thinking you're getting some truncation or similar in your C++ code, not least because -117 is b10001011 in eight-bit 2's complement...which is exactly what we see as the last eight bits of 3979 above.

Opposite of Number.toExponential in JS

I need to get the value of an extremely large number in JavaScript in non-exponential form. Number.toFixed simply returns it in exponential form as a string, which is worse than what I had.
This is what Number.toFixed returns:
>>> x = 1e+31
1e+31
>>> x.toFixed()
"1e+31"
Number.toPrecision also does not work:
>>> x = 1e+31
1e+31
>>> x.toPrecision( 21 )
"9.99999999999999963590e+30"
What I would like is:
>>> x = 1e+31
1e+31
>>> x.toNotExponential()
"10000000000000000000000000000000"
I could write my own parser but I would rather use a native JS method if one exists.
You can use toPrecision with a parameter specifying how many digits you want to display:
x.toPrecision(31)
However, among the browsers I tested, the above code only works on Firefox. According to the ECMAScript specification, the valid range for toPrecision is 1 to 21, and both IE and Chrome throw a RangeError accordingly. This is due to the fact that the floating-point representation used in JavaScript is incapable of actually representing numbers to 31 digits of precision.
Use Number(string)
Example :
var a = Number("1.1e+2");
Return :
a = 110
The answer is there's no such built-in function. I've searched high and low.
Here's the RegExp I use to split the number into sign, coefficient (digits before decimal point), fractional part (digits after decimal point) and exponent:
/^([+-])?(\d+)\.?(\d*)[eE]([+-]?\d+)$/
"Roll your own" is the answer, which you already did.
It's possible to expand JavaScript's exponential output using string functions. Admittedly, what I came up is somewhat cryptic, but it works if the exponent after the e is positive:
var originalNumber = 1e+31;
var splitNumber = originalNumber.toString().split('e');
var result;
if(splitNumber[1]) {
var regexMatch = splitNumber[0].match(/^([^.]+)\.?(.*)$/);
result =
/* integer part */ regexMatch[1] +
/* fractional part */ regexMatch[2] +
/* trailing zeros */ Array(splitNumber[1] - regexMatch[2].length + 1).join('0');
} else result = splitNumber[0];
"10000000000000000000000000000000"?
Hard to believe that anybody would rather look at that than 1.0e+31,
or in html: 1031.
But here's one way, much of it is for negative exponents(fractions):
function longnumberstring(n){
var str, str2= '', data= n.toExponential().replace('.','').split(/e/i);
str= data[0], mag= Number(data[1]);
if(mag>=0 && str.length> mag){
mag+=1;
return str.substring(0, mag)+'.'+str.substring(mag);
}
if(mag<0){
while(++mag) str2+= '0';
return '0.'+str2+str;
}
mag= (mag-str.length)+1;
while(mag> str2.length){
str2+= '0';
}
return str+str2;
}
input: 1e+30
longnumberstring: 1000000000000000000000000000000
to Number: 1e+30
input: 1.456789123456e-30
longnumberstring: 0.000000000000000000000000000001456789123456
to Number: 1.456789123456e-30
input: 1.456789123456e+30
longnumberstring: 1456789123456000000000000000000
to Number: 1.456789123456e+30
input: 1e+80 longnumberstring: 100000000000000000000000000000000000000000000000000000000000000000000000000000000
to Number: 1e+80

Categories

Resources