How do you do large number math in QtQuick Qml using JavaScript - javascript

I want to calculate the circumstance of the Sun around the Galaxy; the Math Formula is ((241828072282107.5071453596951 * 666) * 2) * 3.14159265359, using QML JavaScript I get the answer 1011954093357316100, when the correct answer is 1011954093357316200, a 100 miles off.
galaxyRadius="241828072282107.5071453596951";
currentTrackNumber=666; // Track Number like on a Record
pIe="3.14159265359"; // not the same as Math.PI
I had to use strings because converting these sized numbers to floats truncate the precision, I am converting an old bash script, and it worked fine with bc, not so much Math.
I have tried this:
orbitDist = ((( Number.parseFloat(galaxyRadius).toPrecision(20) * currentTrackNumber) * 2) * Number.parseFloat(pIe).toPrecision(12) );
I get the same results as:
orbitDist = ((( galaxyRadius * currentTrackNumber) * 2) * pIe );
from bash:
echo "$(bc <<< "scale=13;((241828072282107.5071453596951 * 666) * 2) * 3.14159265359")"
Bash is right and JavaScript is wrong by almost a 100 miles, that is crazy, it is freaking me out, how can JavaScript Floating Point Math be so far off, and this is just one example, I have a whole app full of numbers that are close, but not even close enough, a 100 miles off is not acceptable.
I do not want to deal with exponents, I want the value as an Integer, Strings are fine, it is stored in a database as string, I just need the Math to be right.
This is a QtQuick, QML, Felgo App, using Qml and JavaScript, so it will need to run on different platforms. My next thought is C++, or a Math Library that works for this type of project, JavaScript or a QML wrapper Library for C++ would be great, figuring out how make JavaScript not so bad at Floating Point would be better, I found a few JavaScript Libraries for the Web, they do not work without a lot of work under Qml, so I am only interested in what works, and I did a lot of research and did not find what I was looking for.

Because of JavaScript for now just support bignum for integer(BigInt) only. Then I convert it to BigInt before calculate. And tracking the decimal part to re-convert to float.
const galaxyRadius="241828072282107.5071453596951";
const currentTrackNumber=666; // Track Number like on a Record
const pIe="3.14159265359"; // not the same as Math.PI
const orbitDist = ((( galaxyRadius * currentTrackNumber) * 2) * pIe );
console.log(orbitDist)
//// My approad
//Put all number into an array
var arrToMul = [galaxyRadius,currentTrackNumber, 2, pIe]
// Function multiple all item in array with BigInt format
function mulBigInt(arr) {
return arr.reduce(function (acc, e) {
return BigInt(e) * acc;
}, BigInt(1));
};
// Function get length of decimal part
function decLength(str) {
var dec = str.toString().split('.')[1];
return dec ? dec.length : 0;
};
// Function remove point in string to convert float to int
function rmPoint(strNum) {
return strNum.toString().replace('.', "");
};
// Main function
function cal(arr) {
// Get total decimal part length of all number
var pointSize = arr.reduce(function (acc, e) {
return acc + decLength(e);
}, 0);
// convert the all item to int (type string)
var newArr = arr.map(function (e) {
return rmPoint(e);
});
// Add point to reconvert BigInt to float(string actually)
var tmp = mulBigInt(newArr).toString().split('');
tmp.splice(tmp.length - pointSize, 0, '.');
// Return float result with string format
return tmp.join('');
};
const rs = cal(arrToMul)
console.log(rs, 'converted to BigInt:')

Related

How to create a unique value each time when ever I run the java-script code?

I am using Math.random to create a unique value.
However , it looks like after some days , if i run the same script it produces the same value that created earlier.
Is there any way to create unique value every time when ever i run the script.
Below is my code for the random method.
var RandomNo = function (Min,Max){
return Math.floor(Math.random() * (Max - Min + 1)) + Min;
}
module.exports = RandomNo;
The best way to achieve a unique value is to use Date() as milliseconds. This increasing time representation will never repeat.
Do it this way:
var RamdomNo = new Date().getTime();
Done.
Edit
If you are bound to length restrictions, the solution above won't help you as repetition is predictable using an increasing number the shorter it gets.
Then I'd suggest the following approach:
// turn Integer into String. String length = 36
function dec2string (dec) {
return ('0' + dec.toString(36)).substr(-2);
}
// generate a 20 * 2 characters long random string
function generateId () {
var arr = new Uint8Array(20);
window.crypto.getRandomValues(arr);
// return 5 characters of this string starting from position 8.
// here one can increase the quality of randomness by varying
// the position (currently static 8) by another random number <= 35
return Array.from(arr, this.dec2string).join('').substr(8,5);
}
// Test
console.log(generateId());
This pair of methods generates a 40 characters long random string consisting of letters and digits. Then you pick a sequence of 5 consecutive characters off it.

Converting a website's price to a number with no decimals

I have a system that reads prices without decimals.
Example: 2890 = $28.90
I also have a system that takes a websites price of a product with the result being anywhere from $40.25 to just $40 (with no decimals places). I need most likely a regex or a function using javaScript or jQuery that would convert something like $40.25 to 4025 or $40 to 4000. Because I need to send the second system's returning number to the first system which will only except numbers without decimal places.
I originally thought I had it with this: item.price = Number(item.price.replace(/[^0-9\.-]+/g,"")*100); where item.price in this case equals $79.99 but I got a result back as 7998.99999999 instead of 7999 which is what I need and I can't have those decimals places, so parseFloat isn't an option. Would appreciate the help!
Don't re-invent the wheel, use a library! Try https://www.npmjs.com/package/parse-currency
import parseCurrency from 'parse-currency'
const foo = parseCurrency('$10.50')
console.log(foo) // 10.5
const bar = parseCurrency('$1,000,000.25')
console.log(bar) // 1000000.25
As Duncan mentioned, parse-currency library would be the way, but it is not enough for your problem. Let's make a better function...
function parseCurrency(amount) {
var number = amount.replace(/[^\d|\.]/g, ''); // Removes everything that's not a digit or a dot
var parsedToFloat = parseFloat(Math.round(number * 100) / 100); // Make a float number even it is an integer
return parsedToFloat.toFixed(2); // Now make sure that it will have always 2 decimal places
}
// This will return the following results...
parseCurrency('$40'); // "40.00"
parseCurrency('$40.25'); // "40.25"
parseCurrency('$40,000.25'); // "40000.25"
As you asked for a number that won't be fixed, you can do something like that:
const currencies = [
'$40',
'$45.25',
'$45.251123456789',
'$1,000',
'$1,000.25'
];
function convertToNumber(currency) {
const number = currency.replace(/[^\d|\.]/g, '');
return parseFloat(parseFloat(number).toFixed(2)) * 100;
}
console.log(currencies.map(convertToNumber))

How to simulate the result of a Google Sheet multiplication formula in Javascript?

If the value of f5 cell in a Google Sheet is 1.1000 (a number formatted to 4 decimal places) and the value of f6 is = f5 * 1.073, how can I ensure I get the same result multiplying those values in Javascript, eg:
var original_value = 1.1000;
var derivative_value = original_value * 1.073;
Specifically, my question is - will the result of the Javascript multiplication (derivative_value) be the same as the result of the Google formula (f6)? And if not, how can I make it so that it is?
Context / What I've Tried
For context, this question is part of a larger question I am trying to resolve for which I have set up this JSFiddle.
The JSFiddle has an input for the original_value and an input for the multiplier.
It outputs the result to four decimal places and adds trailing zeros where required (this is the required format for the result).
It is an attempt to check that the Javascript code I am writing will produce the same result as the Google Sheet formula.
[ The JSFiddle has been updated to also log decimal.js results to the console for comparison ]
Edit
There was a suggestion to use decimal.js but I'm not sure how it would be applied - something like the following?
var original_value = new Decimal(1.1000);
// some different multipliers for testing
var multiplier_a = new Decimal(1.073);
var multiplier_b = new Decimal(1.1);
// some different results for testing
var derivative_value_a = original_value.times(multiplier_a).toString();
var derivative_value_b = original_value.times(multiplier_b).toString();
console.log(derivative_value_a); // 1.1803
console.log(derivative_value_b); // 1.21
Is that any more accurate than plain Javascript original_value * multiplier? More importantly for this question, will it always simulate the same result that a Google Sheet formula produces?
JavaScript is using so called double precision float format (64 bit)- https://tc39.github.io/ecma262/#sec-terms-and-definitions-number-value
Google Sheets seem to use the same format, you can test it by =f6*1E13 - round(f6*1E13) to see that f6 is not STORED as a fixed number format, only FORMATTED
see Number.toFixed how to FORMAT numbers in Javascript
to generate some test data:
[...Array(10)].forEach(() => {
const f5 = 1.1
const x = Math.random() / 100
const f6 = f5 * x
console.log(x, f6.toFixed(4))
})
and compare in Google Sheet:
https://docs.google.com/spreadsheets/d/1jKBwzM41nwIEyatLUHEUwteK8ImJg334hzJ8nKkUZ5M/view
=> all rounded numbers are equal.
P.S.: you need to copy the console output, paste into the Sheet, use the menu item Data > Split text into columns... > Space, then multiply by 1.1 in 3rd column and finally format all numbers
After revisiting this I have updated the jsFiddle.
The main components of what I believe are a satisfactory solution are:
Convert both original_value and multiplier to decimal.js objects.
Do the multiplication using the decimal.js times method.
Do the rounding using the decimal.js toDecimalPlaces method.
Use the argument values (4,7) to define 4 decimal places with ROUND_HALF_CEIL rounding, equivalent to Math.round (reference)
For example:
var my_decimal_js_value = new Decimal(original_value).times(new Decimal(multiplier)).toDecimalPlaces(4, 7);
In order to add any necessary trailing zeros to the result, I use:
function trailingZeros(my_decimal_js_value) {
var result = my_decimal_js_value;
// add zeros if required:
var split_result = result.toString().split(".");
// if there are decimals present
if (split_result[1] != undefined) {
// declare trailing_zeros;
var trailing_zeros;
// get the amount of decimal numbers
decimals_present = split_result[1].length;
// if one decimal number, add three trailing zeros
if (decimals_present === 1) {
trailing_zeros = "000";
result += trailing_zeros;
}
// if two decimal numbers, add two trailing zeros
else if (decimals_present === 2) {
trailing_zeros = "00";
result += trailing_zeros;
}
// if three decimal numbers, add one trailing zero
else if (decimals_present === 3) {
trailing_zeros = "0";
result += trailing_zeros;
}
// if four decimal numbers, just convert result to string
else if (decimals_present === 4) {
result = result.toString();
}
}
// if there are no decimals present, add a decimal place and four zeros
else if (split_result[1] === undefined) {
trailing_zeros = ".0000";
result += trailing_zeros;
}
return result;
}
I am still not absolutely certain that this mimics the Google Sheet multiplication formula, however using decimal.js, or another dedicated decimal library, seems to be the preferred method over plain JavaScript (to avoid possible rounding errors), based on posts such as these:
http://www.jacklmoore.com/notes/rounding-in-javascript
Is floating point math broken?
https://spin.atomicobject.com/2016/01/04/javascript-math-precision-decimals

Convert A Large Integer To a Hex String In Javascript

I need to find a way to convert a large number into a hex string in javascript. Straight off the bat, I tried myBigNumber.toString(16) but if myBigNumber has a very large value (eg 1298925419114529174706173) then myBigNumber.toString(16) will return an erroneous result, which is just brilliant. I tried writing by own function as follows:
function (integer) {
var result = '';
while (integer) {
result = (integer % 16).toString(16) + result;
integer = Math.floor(integer / 16);
}
}
However, large numbers modulo 16 all return 0 (I think this fundamental issue is what is causing the problem with toString. I also tried replacing (integer % 16) with (integer - 16 * Math.floor(integer/16)) but that had the same issue.
I have also looked at the Big Integer Javascript library but that is a huge plugin for one, hopefully relatively straightforward problem.
Any thoughts as to how I can get a valid result? Maybe some sort of divide and conquer approach? I am really rather stuck here.
Assuming you have your integer stored as a decimal string like '1298925419114529174706173':
function dec2hex(str){ // .toString(16) only works up to 2^53
var dec = str.toString().split(''), sum = [], hex = [], i, s
while(dec.length){
s = 1 * dec.shift()
for(i = 0; s || i < sum.length; i++){
s += (sum[i] || 0) * 10
sum[i] = s % 16
s = (s - sum[i]) / 16
}
}
while(sum.length){
hex.push(sum.pop().toString(16))
}
return hex.join('')
}
The numbers in question are above javascript's largest integer. However, you can work with such large numbers by strings and there are some plugins which can help you do this. An example which is particularly useful in this circumstance is hex2dec
The approach I took was to use the bignumber.js library and create a BigNumber passing in the value as a string then just use toString to convert to hex:
const BigNumber = require('bignumber.js');
const lrgIntStr = '1298925419114529174706173';
const bn = new BigNumber(lrgIntStr);
const hex = bn.toString(16);

Math.random() returns value greater than one?

While playing around with random numbers in JavaScript I discovered a surprising bug, presumably in the V8 JavaScript engine in Google Chrome. Consider:
// Generate a random number [1,5].
var rand5 = function() {
return parseInt(Math.random() * 5) + 1;
};
// Return a sample distribution over MAX times.
var testRand5 = function(dist, max) {
if (!dist) { dist = {}; }
if (!max) { max = 5000000; }
for (var i=0; i<max; i++) {
var r = rand5();
dist[r] = (dist[r] || 0) + 1;
}
return dist;
};
Now when I run testRand5() I get the following results (of course, differing slightly with each run, you might need to set "max" to a higher value to reveal the bug):
var d = testRand5();
d = {
1: 1002797,
2: 998803,
3: 999541,
4: 1000851,
5: 998007,
10: 1 // XXX: Math.random() returned 4.5?!
}
Interestingly, I see comparable results in node.js, leading me to believe it's not specific to Chrome. Sometimes there are different or multiple mystery values (7, 9, etc).
Can anyone explain why I might be getting the results I see? I'm guessing it has something to do with using parseInt (instead of Math.floor()) but I'm still not sure why it could happen.
The edge case occurs when you happen to generate a very small number, expressed with an exponent, like this for example 9.546056389808655e-8.
Combined with parseInt, which interprets the argument as a string, hell breaks loose. And as suggested before me, it can be solved using Math.floor.
Try it yourself with this piece of code:
var test = 9.546056389808655e-8;
console.log(test); // prints 9.546056389808655e-8
console.log(parseInt(test)); // prints 9 - oh noes!
console.log(Math.floor(test)) // prints 0 - this is better
Of course, it's a parseInt() gotcha. It converts its argument to a string first, and that can force scientific notation which will cause parseInt to do something like this:
var x = 0.000000004;
(x).toString(); // => "4e-9"
parseInt(x); // => 4
Silly me...
I would suggest changing your random number function to this:
var rand5 = function() {
return(Math.floor(Math.random() * 5) + 1);
};
This will reliably generate an integer value between 1 and 5 inclusive.
You can see your test function in action here: http://jsfiddle.net/jfriend00/FCzjF/.
In this case, parseInt isn't the best choice because it's going to convert your float to a string which can be a number of different formats (including scientific notation) and then try to parse an integer out of it. Much better to just operate on the float directly with Math.floor().

Categories