-
-
Notifications
You must be signed in to change notification settings - Fork 932
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
datatype.number does not always generate numbers with expected precision #331
Comments
After I had some look into this, I noticed this only happens with precision of 0.0001. Then I came up with some simple code to just truncate the extra digits from the output. This way, we won't have to use The code is in javascript, and probably not perfect and certainly a hacky approach, but it does cover all edge cases: function getDecimals(a) {
if (!isFinite(a)) return 0;
let e = 1, p = 0;
while (Math.round(a * e) / e !== a) { e *= 10; p++; }
return p;
}
function toFixed(x) {
if (Math.abs(x) < 1.0) {
const e = parseInt(x.toString().split('e-')[1]);
if (e) {
x *= Math.pow(10,e-1);
x = '0.' + (new Array(e)).join('0') + x.toString().substring(2);
}
} else {
const e = parseInt(x.toString().split('+')[1]);
if (e > 20) {
e -= 20;
x /= Math.pow(10,e);
x += (new Array(e+1)).join('0');
}
}
return x;
}
function toFixedTrunc(x, n) {
x = toFixed(x)
const v = (typeof x === 'string' ? x : x.toString()).split('.');
if (n <= 0) return v[0];
let f = v[1] || '';
if (f.length > n) return `${v[0]}.${f.substr(0,n)}`;
while (f.length < n) f += '0';
return `${v[0]}.${f}`;
}
const { faker } = require('@faker-js/faker');
const precision = 0.00001;
const decimals = getDecimals(precision);
function generate() {
const generatedNumber = faker.datatype.number({ precision });
return getDecimals(generatedNumber) <= decimals ? generatedNumber : toFixedTrunc(generatedNumber, decimals)
}
let counter = 0;
while(counter < 10) {
console.log(generate())
counter++;
}
/*
Output:
32320.83572
37150.50227
74120.61501
97321.44276
86947.56555
98595.50214
24725.63345
40594.42884
921.41321
41778.05577
*/ repl here: https://replit.com/@luciferreeves/FrozenNiftyCategories#index.js |
The issue is not only on
12 precision points, when 9 was requested. Big.js is not a large library and I think it does not make sense for us to battle with float arithmetics. |
Even though the problem is with
Update:I modified the generate method in the code above to take a precision: function generate(precision) {
const generatedNumber = faker.datatype.number({ precision });
// This code from here goes into the faker.datatype.number implementation:
const decimals = getDecimals(precision);
return getDecimals(generatedNumber) <= decimals ? generatedNumber : toFixedTrunc(generatedNumber, decimals)
} then, I added a simple test, generated 10000 numbers of the same precision and I did it for all precisions (upto 16 decimal places) and none of them fail: // Here we test the precisions
let precision = 1;
let precisionArray = [];
let ctr = 0;
while(ctr !== 17) {
precisionArray.push(precision);
// this is equivalent of doing precision = precision/10 but with accurate results.
precision = parseFloat(`0.${'0'.repeat(ctr)}1`);
ctr += 1;
}
precisionArray.forEach(p => {
process.stdout.write(`Precision: ${p.toExponential()} \t`)
const fail = false;
let counter = 1
while(counter <= 10000) {
const num = generate(precision);
if (getDecimals(num) > getDecimals(precision)) {
console.log(`Test ${counter} failed for precision ${precision}, generated number: ${num}.`)
fail = true;
break;
}
counter++;
}
if(!fail) {
process.stdout.write(`PASS \n`)
} else {
process.stdout.write(`FAIL \n`)
}
}); Output:
I also updated the repl, so you can try this out yourself. I think this method fixes the bug. |
How many digits of precision should we support? |
https://en.wikipedia.org/wiki/Double-precision_floating-point_format IMO we should support a max precision of 15 digits, definitely 14 digits. Everything more precise I would highly suggest to use a different library that is more targeted to the use-case. |
If we would limit ourselves to 12 digits, then a simple regex replace would do the job for us. |
Any number would do for me as long as it works ;) |
Heads up here, but I made this ticket a few weeks ago on the same matter. Are we expected to see this change in the next update? |
Fixed by #2581 at least for precisions in the form of This will be in the next release (v8.4). |
Describe the bug
Due to the limitations of float arithmetics the
precision
param is not always well reflected in the generated output.Reproduction
Additional Info
Suggested solution would be to use a better math library offering arbitrary-precision decimal arithmetic, eg. https://www.npmjs.com/package/big.js
The text was updated successfully, but these errors were encountered: