Javascript function is mutating parameter with node v14.7.0 - node.js

Node.js is mutating the number value passed into the test function. Any idea why and how to fix? Thank you!
const val1 = 61368443939717130;
const val2 = 861368443939717130;
const val3 = 161368443939717130;
const test = (v) => {
console.log(v);
}
test(val1);
test(val2);
test(val3);
// Output:
// 61368443939717130
// 861368443939717100
// 161368443939717120

It's because the number 161368443939717130 is bigger than the maximum safe integer that Javascript can represent, which is provided by the constant Number.MAX_SAFE_INTEGER. When working with big numbers you should always check that the values are below the limit, or otherwise you will get unexpected results. See more info here.

Your numbers are out of range for javascript.
The max number javascript can handle is 9007199254740991 (9,007,199,254,740,991 or ~9 quadrillion).
It is represented by the MAX_SAFE_INTEGER variable. The reasoning behind that number is that JavaScript uses double-precision floating-point format numbers as specified in IEEE 754 and can only safely represent integers between -(2^53 - 1) and 2^53 - 1.
Use BigInt, which has no practical upper limit. But BigInt can’t handle decimals.
This means that if you convert from a Number to a BigInt and backward again, you can lose precision.
console.log(Number.MAX_SAFE_INTEGER)
// 9007199254740991
console.log(Number.MAX_SAFE_INTEGER + 10)
// 9007199254741000
console.log(BigInt(Number.MAX_SAFE_INTEGER) + 10n)
// 9007199254741001n

Related

Express - Retain scale of number even if 0

Using express, is it possible to return 0.0 (as a number) in response JSON?
What we are observing is, if the value is 0.0, then Javascript is keeping only 0 and response JSON just has 0.
We have a consuming system which is implying the data type of the json property based on data and it is wrongly implying the field to be Long instead of Decimal.
you can use .toFixed() to set the number of decimals. So for example
var number = 0.000;
console.log(number); // <--- Prints only 0
var preciseNumber = parseFloat(0).toFixed(4); // 4 inside the .toFixed(4) is number of places after decimal point
console.log(preciseNumber); // <--- Prints '0.0000'
Do note that the preciseNumber is a string now.
you can use .toFixed() to set the number of decimals. So for example
var number = 0.000;
console.log(number); // <--- Prints only 0
var preciseNumber = parseFloat(0).toFixed(4); // 4 inside the .toFixed(4) is number of places after decimal point
console.log(preciseNumber); // <--- Prints '0.0000'
Do note that the preciseNumber is a string now.

Write large number to JSON file

I am trying to do a relatively simple task of writing 1000000000000000000000 (as a number without quotes) to a json file using NodeJs. I have this number defined as a constant:
const NUM = 1000000000000000000000
If I simply write it, it comes out as 1e+21. Everything else I tried (big number...) ends up converting it to a string.
Can someone please help?
Update
To clarify what i am after, please look at the code below:
// 22 digits
const bigNum = 1000000000000000000000;
console.log(JSON.stringify(bigNum)); // Output: 1e+21
// 19 digits
const bigNum2 = 1000000000000000000;
console.log(JSON.stringify(bigNum2)); // Output: 1000000000000000000
What i would like is to be able to output 1000000000000000000000 in the first example instead of 1e+21
If I simply write it, it comes out as 1e+21. Everything else I tried (big number...) ends up converting it to a string.
JSON will always be a string in its serialized representation. It also does not impose any limits on number sizes, although you might of course hit limits imposed by the runtime you use to serialize your data into JSON:
> let num = JSON.stringify(Number.MAX_SAFE_INTEGER + 1)
undefined
> JSON.parse(num)
9007199254740992
> Number.MAX_SAFE_INTEGER + 1
9007199254740992
The exponential notation does not change the value but just saves bytes and is easier to read:
> 1e+21 === 1000000000000000000000
true

Provide Decimal Point Number to .toLocaleString() in TypeScript

I am trying to form a number having a decimal separator from a string in order to be able to apply .toLocaleString() method , which cannot be applied on strings.
const initialString = 122; //number
const decimalStr = initialString.toFixed(initialString); //string
const formattedStr = decimalStr.toLocaleString('de-DE');//error,decimalStr is a string
If any conversion is to be applied to the decimalStr, the decimal digits would be lost ( i.e. decimalStr = +decimalStr or decimalStr = Number(decimalStr). => problem, need to keep decimal digits.
How can i keep the decimal points and make the .toLocaleString()
see calling value as a number?
The Number.toLocaleString method accepts a second argument called options.
You can use this argument to control the number of fraction digits. Therefore, your call would be:
const initialNumber = 122;
const formattedStr = initialNumber.toLocaleString('de-DE', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
For more details check here.
Also, notice that both toFixed and the maximumFractionDigits have limits: 100 and 20, respectively. So the example you provide fails on the second line.

How to convert padbytes function to coldfusion

I have the following code in node and I am trying to convert to ColdFusion:
// a correct implementation of PKCS7. The rijndael js has a PKCS7 padding already implemented
// however, it incorrectly pads expecting the phrase to be multiples of 32 bytes when it should pad based on multiples
// 16 bytes. Also, every javascript implementation of PKCS7 assumes utf-8 char encoding. C# however is unicode or utf-16.
// This means that chars need to be treated in our code as 2 byte chars and not 1 byte chars.
function padBytes(string){
const strArray = [...new Buffer(string, 'ucs2')];
const need = 16 - ((strArray.length) % 16);
for(let i = 0; i < need; i++) {
strArray.push(need);
}
return Buffer.from(strArray);
}
I'm trying to understand exactly what this function is doing to convert it. As I think I understand it, it's converting the string to UTF-16 (UCS2) and then adding padding to each character. However, I don't understand why the need variable is the value it is, nor how exactly to achieve that in CF.
I also don't understand why it's only pushing the same value into the array over and over again. For starters, in my example script the string is 2018-06-14T15:44:10Z testaccount. The string array length is 64. I'm not sure how to achieve even that in CF.
I've tried character encoding, converting to binary and stuff to UTF-16 and just don't understand well enough the js function to replicate it in ColdFusion. I feel I'm missing something with the encoding.
EDIT:
The selected answer solves this problem, but because I was eventually trying to use the input data for encryption, the easier method was to not use this function at all but do the following:
<cfset stringToEncrypt = charsetDecode(input,"utf-16le") />
<cfset variables.result = EncryptBinary(stringToEncrypt, theKey, theAlgorithm, theIV) />
Update:
We followed up in chat and turns out the value is ultimately used with encrypt(). Since encrypt() already handles padding (automatically), no need for the custom padBytes() function. However, it did require switching to the less commonly used encryptBinary() function to maintain the UTF-16 encoding. The regular encrypt() function only handles UTF-8, which produces totally different results.
Trycf.com Example:
// Result with sample key/iv: P22lWwtD8pDrNdQGRb2T/w==
result = encrypt("abc", theKey, theAlgorithm, theEncoding, theIV);
// Result Result with sample key/iv: LJCROj8trkXVq1Q8SQNrbA==
input = charsetDecode("abc", "utf-16le");
result= binaryEncode(encryptBinary(input, theKey, theAlgorithm, theIV), "base64);
it's converting the string to utf-16
(ucs2) and then adding padding to each character.
... I feel I'm missing something with the encoding.
Yes, the first part seems to be decoding the string as UTF-16 (or UCS2 which are slightly different). As to what you're missing, you're not the only one. I couldn't get it to work either until I found this comment which explained "UTF-16" prepends a BOM. To omit the BOM, use either "UTF-16BE" or "UTF-16LE" depending on the endianess needed.
why it's only pushing the same value into the array over and over again.
Because that's the definition of PCKS7 padding. Instead of padding with something like nulls or zeroes, it calculates how many bytes padding are needed. Then uses that number as the padding value. For example, say a string needs an extra three bytes padding. PCKS7 appends the value 3 - three times: "string" + "3" + "3" + "3".
The rest of the code is similar in CF. Unfortunately, the results of charsetDecode() aren't mutable. You must build a separate array to hold the padding, then combine the two.
Note, this example combines the arrays using CF2016 specific syntax, but it could also be done with a simple loop instead
Function:
function padBytes(string text){
var combined = [];
var padding = [];
// decode as utf-16
var decoded = charsetDecode(arguments.text,"utf-16le");
// how many padding bytes are needed?
var need = 16 - (arrayLen(decoded) % 16);
// fill array with any padding bytes
for(var i = 0; i < need; i++) {
padding.append(need);
}
// concatenate the two arrays
// CF2016+ specific syntax. For earlier versions, use a loop
combined = combined.append(decoded, true);
combined = combined.append(padding, true);
return combined;
}
Usage:
result = padBytes("2018-06-14T15:44:10Z testaccount");
writeDump(binaryEncode( javacast("byte[]", result), "base64"));

express-cassandra is converting Long value into negative

I am using express-cassandra npm package to connect to database, The below code is giving negative value while converting Long value into Integer.
var num = 13315766168394088000;
Result is:-
var valueFromInt = models.datatypes.Long.fromInt(num);
=> Long: -1152696320
var valueFromString = models.datatypes.Long.fromString(num.toString());
=> Long: -5130977905315463616
I don't understand why this is huge difference and why this is returning negative value.
13315766168394088000 is greater than Number.MAX_SAFE_INTEGER (2^53), so you should not represent it with a Number (64-bit IEEE-754 double) and you shouldn't use Long.fromInt().
You could work with either the bits or the decimal string representation of the value:
const longValue = Long.fromString('13315766168394088000');

Resources