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
Related
I am writing a function to implement a LFSR in Python 3. I've figured out the hard part, which is actually stepping through the state using the feedback value to get the key and getting all of the output bytes by ANDing the key and the input data. To make it easier to do bit shifts and OR and XOR operations, I did all of my operations on integers and saved the result as an integer, however, the function requires a return type of bytes. I have been doing some Googling of my own, and it seems like an accepted way to convert an integer to a bytes object is to do something like result_bytes = result_bytes.to_bytes((result_bytes.bit_length() + 7) // 8, 'big'). Running hex_result_bytes = hex(result_bytes) (mind you, in this context, result_bytes is still currently an integer) and my result_bytes is an integer with a value of 3187993425, I get a str result of "0xbe04eb51", so clearly I should expect a bytes object that looks like b'\xbe\x04\xeb\x51'.
After running result_bytes = result_bytes.to_bytes((result_bytes.bit_length() + 7) // 8, 'big'), I should expect b'\xbe\x04\xeb\x51'. Instead, the result I get is b'\xbe\x04\xebQ' in the PyCharm debugger. Is there something obvious that I am missing here? I don't even know how I got Q because Q is clearly not something that you can get as a hexadecimal byte.
I'm not sure why the PyCharm debugger does this, but it seems to be converting part of the binary string to ASCII. I ran an expression to see if b'\xbe\x04\xebQ' == b'\xbe\x04\xeb\x51', and it evaluates to True. 51 in hex corresponds to Q in ASCII. Another binary string that I am working with right now should be b'\x48\xDC\x40\xD1\x4C', but it is also converting part of it into ASCII and showing it as b'H\xdc#\xd1L', where hex 48 corresponds to H in ASCII, hex 40 corresponds to # in ASCII, and hex 4C corresponds to L in ASCII
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
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"));
I currently have a string that can be any length in size based on a single digit in one or two specific locations (based on the first digit captured). For example:
Changed
First digit captured tells me IF a file name is to follow: "1" = Object Name Follows. "0" = Next input captured is Length Multiplier.
"1" is not always received. But "0" is always received.
With "1" Capture it looks like this:
START|(1)|NAMEOFGRAPHIC|(0)|(#)|INPUT|INPUT|INPUT|INPUT|... etc
With "0" (no "1" captured)
START|(0)|(#)|INPUT|INPUT|INPUT|INPUT|... etc
The Length Multiplier bit (always follows "0") is the number of INPUT groups to follow. A "group" is a set of 4xINPUT's. So, if it was a "4", the string I want to completely capture looks like this:
With a "1":
START|(1)|NAMEOFGRAPHIC|(0)|(4)|INPUT|INPUT|INPUT|INPUT|INPUT|INPUT|INPUT|INPUT|INPUT|INPUT|INPUT|INPUT|INPUT|INPUT|INPUT|INPUT|
With a "0":
START|(0)|(4)|INPUT|INPUT|INPUT|INPUT|INPUT|INPUT|INPUT|INPUT|INPUT|INPUT|INPUT|INPUT|INPUT|INPUT|INPUT|INPUT|
As each INPUT is received, a pipe symbol is added after. I want to use the pipes to monitor the length of the input based on the digit. If the digit is 5, for example, it would capture the 3x INPUT, 5, then 5x INPUT after (with all pipes included). Once this is done, the function would send the fully captured string to other function(s) for use.
I am having problems working out the receiving function to capture this full string. I have tried to count the number of pipes in different loop functions and all are resulting in errors.
Attempts include (please understand I'm pretty new to all of this):
local buffer = ""
function pipe_count(input)
a = "|"
buffer = buffer..input.."|"
while #a < 5 do
buffer = buffer..input.."|"
return buffer
end
end
local buffer = ""
function pipe_count(input)
buffer = buffer..input.."|"
mult = tonumber(buffer:match("(.-|.-|.-|(%d)|.*)"))
while buffer do
for i = 1, mult do
buffer = buffer..input.."|"
end
return buffer
end
Those were two examples I tried. I deleted my other futile attempts to capture the exact string length. My current issue that it is taking the INPUT captures, as each one is received, and sending it to the next function prior to capturing the entire string. So, if I had received the string at the top, it would look like this:
`INPUT`
`INPUT|INPUT`
`INPUT|INPUT|INPUT`
`INPUT|INPUT|INPUT|5`
`INPUT|INPUT|INPUT|5|INPUT`
`INPUT|INPUT|INPUT|5|INPUT|INPUT` etc
until finally the string below is received:
`INPUT|INPUT|INPUT|5|INPUT|INPUT|INPUT|INPUT|INPUT|`
At this point, my file runs as it should. But up until this point, I'm getting errors since the parameters of the function(s) aren't fully met.
Ideally, I want that last string before moving on.
Any ideas would be very welcomed and appreciated.
Cheers
ETA: These INPUT's are filling a buffer. I want that check digit to be responsible for the string to only be used if the length value is met. Again, I really appreciate all input. Thank you.
ETA: Example code tried and more input details.
All strings in Lua are internalized, so it's usually a better idea to push strings onto an array than to repeatedly rebuild the same string. This example takes input line by line from stdin. 3 data inputs, followed by a number, followed by that number of data inputs. There are plenty of other ways to do it, but this is pretty easy to follow.
local buffer = {}
function process_input(input)
if #buffer == 3 then
input = tonumber(input)
end
table.insert(buffer,input)
if #buffer > 4 and #buffer == buffer[4] + 4 then
local pipe_delim = table.concat(buffer,'|')
buffer = {}
return pipe_delim
end
end
repeat
local input = io.read()
local pipe_delim = process_input( input )
if pipe_delim then
print('Got:', pipe_delim)
end
until false
My data looks like this
-3442.77 -16749.64 893.08 -3442.77 -16749.64 1487.35 -3231.45 -16622.36 902.29
.....
159*2539.87 10*0.00 162*2539.87 10*0.00
which means I start with either 7 or 8 reals per line and then (towards the end) have 159 values of 2539.87 followed by 10 values of 0 followed by 162 of 2539.87 etc. This seems to be a space-saving method as previous versions of this file format were regular 6 reals per line.
I am already reading the data into a string because of not knowing whether there are 7 or 8 numbers per line. I can therefore easily spot lines that contain *. But what then? I suppose I have to identify the location of each * and then identify the integer number before and real value after before assigning to an array. Am I missing anything?
Read the line. Split it into tokens delimited by whitespace(s). Replace the * in tokens that have it with space. Then read from the string one or two values, depending on wheather there was an asterisk or not. Sample code follows:
REAL, DIMENSION(big) :: data
CHARACTER(LEN=40) :: token
INTEGER :: iptr, count, idx
REAL :: val
iptr = 1
DO WHILE (there_are_tokens_left)
... ! Get the next token into "token"
idx = INDEX(token, "*")
IF (idx == 0) THEN
READ(token, *) val
count = 1
ELSE
! Replace "*" with space and read two values from the string
token(idx:idx) = " "
READ(token, *) count, val
END IF
data(iptr:iptr+count-1) = val ! Add "val" "count" times to the list of values
iptr = iptr + count
END DO
Here I have arbitrarily set the length of the token to be 40 characters. Adjust it according to what you expect to find in your input files.
BTW, for the sake of completeness, this method of compressing something by replacing repeating values with value/repetition-count pairs is called run-length encoding (RLE).
Your input data may have been written in a form suitable for list directed input (where the format specification in the READ statement is simply ''*''). List directed input supports the r*c form that you see, where r is a repeat count and c is the constant to be repeated.
If the total number of input items is known in advance (perhaps it is fixed for that program, perhaps it is defined by earlier entries in the file) then reading the file is as simple as:
REAL :: data(size_of_data)
READ (unit, *) data
For example, for the last line shown in your example on its own ''size_of_data'' would need to be 341, from 159+10+162+10.
With list directed input the data can span across multiple records (multiple lines) - you don't need to know how many items are on each line in advance - just how many appear in the next "block" of data.
List directed input has a few other "features" like this, which is why it is generally not a good idea to use it to parse "arbitrary" input that hasn't been written with it in mind - use an explicit format specification instead (which may require creating the format specification on the fly to match the width of the input field if that is not know ahead of time).
If you don't know (or cannot calculate) the number of items in advance of the READ statement then you will need to do the parsing of the line yourself.