Skip to content
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

feat(number)!: default to high precision float #1675

Merged
merged 17 commits into from
Jan 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions docs/guide/upgrading.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,20 @@ The `faker.address.*` methods will continue to work as an alias in v8 and v9, bu
The number-related methods previously found in `faker.datatype` have been moved to a new `faker.number` module.
For the old `faker.datatype.number` method you should replace with `faker.number.int` or `faker.number.float` depending on the precision required.

faker.datatype.number() //35
faker.datatype.int() //35

faker.datatype.number({precision:0.01}) //35.21
faker.datatype.float({precision:0.01}) //35.21
By default, `faker.number.float` no longer defaults to a precision of 0.01

```js
// OLD
faker.datatype.number({ max: 100 }); // 35
faker.datatype.number({ max: 100, precision: 0.01 }); // 35.21
faker.datatype.float({ max: 100 }); // 35.21
faker.datatype.float({ max: 100, precision: 0.001 }); // 35.211

// NEW
faker.number.int({ max: 100 }); // 35
faker.number.float({ max: 100 }); // 35.21092065742612
faker.number.float({ max: 100, precision: 0.01 }); // 35.21
```

| Old method | New method |
| ----------------------- | ------------------------------------------ |
Expand Down
14 changes: 5 additions & 9 deletions src/internal/mersenne/mersenne.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,10 @@ import Twister from './twister';
*/
export interface Mersenne {
/**
* Generates a random number between `[min, max)`. The result is already floored.
*
* @param options The options to generate a random number.
* @param options.min The minimum number.
* @param options.max The maximum number.
* Generates a random float between `[0, 1)`.
* This method is called `next` so that it could be used as an [iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterator_protocol)
*/
next(options: { max: number; min: number }): number;
next(): number;

/**
* Sets the seed to use.
Expand All @@ -34,10 +31,9 @@ export default function mersenne(): Mersenne {
twister.initGenrand(Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER));

return {
next({ min, max }): number {
return Math.floor(twister.genrandReal2() * (max - min) + min);
next(): number {
return twister.genrandReal2();
},

seed(seed: number | number[]): void {
if (typeof seed === 'number') {
twister.initGenrand(seed);
Expand Down
8 changes: 4 additions & 4 deletions src/modules/color/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ export class ColorModule {

color = Array.from({ length: 3 }, () => this.faker.number.int(255));
if (includeAlpha) {
color.push(this.faker.number.float());
color.push(this.faker.number.float({ precision: 0.01 }));
cssFunction = 'rgba';
}

Expand Down Expand Up @@ -458,7 +458,7 @@ export class ColorModule {
}): string | number[];
cmyk(options?: { format?: ColorFormat }): string | number[] {
const color: string | number[] = Array.from({ length: 4 }, () =>
this.faker.number.float()
this.faker.number.float({ precision: 0.01 })
);
return toColorFormat(color, options?.format || 'decimal', 'cmyk');
}
Expand Down Expand Up @@ -568,7 +568,7 @@ export class ColorModule {
}): string | number[] {
const hsl: number[] = [this.faker.number.int(360)];
for (let i = 0; i < (options?.includeAlpha ? 3 : 2); i++) {
hsl.push(this.faker.number.float());
hsl.push(this.faker.number.float({ precision: 0.01 }));
}

return toColorFormat(
Expand Down Expand Up @@ -674,7 +674,7 @@ export class ColorModule {
}): string | number[] {
const hsl: number[] = [this.faker.number.int(360)];
for (let i = 0; i < 2; i++) {
hsl.push(this.faker.number.float());
hsl.push(this.faker.number.float({ precision: 0.01 }));
}

return toColorFormat(hsl, options?.format || 'decimal', 'hwb');
Expand Down
38 changes: 24 additions & 14 deletions src/modules/number/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ export class NumberModule {
const mersenne: Mersenne =
// @ts-expect-error: access private member field
this.faker._mersenne;

return mersenne.next({ min: effectiveMin, max: effectiveMax + 1 });
const real = mersenne.next();
return Math.floor(real * (effectiveMax + 1 - effectiveMin) + effectiveMin);
xDivisionByZerox marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand All @@ -91,13 +91,13 @@ export class NumberModule {
* @param options Upper bound or options object. Defaults to `{}`.
* @param options.min Lower bound for generated number. Defaults to `0.0`.
* @param options.max Upper bound for generated number. Defaults to `1.0`.
* @param options.precision Precision of the generated number. Defaults to `0.01`.
* @param options.precision Precision of the generated number, for example `0.01` will round to 2 decimal points.
*
* @example
* faker.number.float() // 0.89
* faker.number.float(3) // 1.14
* faker.number.float({ min: -1000000 }) // -823469.91
* faker.number.float({ max: 100 }) // 27.28
* faker.number.float() // 0.5688541042618454
* faker.number.float(3) // 2.367973240558058
* faker.number.float({ min: -1000000 }) //-780678.849672846
* faker.number.float({ max: 100 }) // 17.3687307164073
* faker.number.float({ precision: 0.1 }) // 0.9
* faker.number.float({ min: 10, max: 100, precision: 0.001 }) // 35.415
*
Expand Down Expand Up @@ -133,7 +133,7 @@ export class NumberModule {
};
}

const { min = 0, max = 1, precision = 0.01 } = options;
const { min = 0, max = 1, precision } = options;

if (max === min) {
return min;
Expand All @@ -143,13 +143,23 @@ export class NumberModule {
throw new FakerError(`Max ${max} should be greater than min ${min}.`);
}

const factor = 1 / precision;
const int = this.int({
min: min * factor,
max: max * factor,
});
if (precision !== undefined) {
ST-DDT marked this conversation as resolved.
Show resolved Hide resolved
xDivisionByZerox marked this conversation as resolved.
Show resolved Hide resolved
if (precision <= 0) {
throw new FakerError(`Precision should be greater than 0.`);
}

return int / factor;
const factor = 1 / precision;
xDivisionByZerox marked this conversation as resolved.
Show resolved Hide resolved
const int = this.int({
min: min * factor,
max: max * factor,
});
return int / factor;
} else {
// @ts-expect-error: access private member field
const mersenne: Mersenne = this.faker._mersenne;
const real = mersenne.next();
return real * (max - min) + min;
}
}

/**
Expand Down
6 changes: 3 additions & 3 deletions test/__snapshots__/datatype.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -267,8 +267,8 @@ exports[`datatype > 1337 > array > noArgs 1`] = `
"e|/Jqjjj!B",
"GDWQgC2M;q",
3648103756333056,
3967686428065792,
".Gm3tRwnZ2",
"I1.Gm3tRwn",
1668592164667392,
8623125245722624,
3831794621218816,
]
Expand Down Expand Up @@ -351,7 +351,7 @@ exports[`datatype > 1337 > hexadecimal > with length, prefix, and casing 1`] = `

exports[`datatype > 1337 > hexadecimal > with prefix 1`] = `"0x5"`;

exports[`datatype > 1337 > json 1`] = `"{\\"foo\\":\\"U/4:SK$>6Q\\",\\"bar\\":2359372120326144,\\"bike\\":\\"{:e=+kD)[B\\",\\"a\\":\\"e|/Jqjjj!B\\",\\"b\\":\\"GDWQgC2M;q\\",\\"name\\":3648103756333056,\\"prop\\":3967686428065792}"`;
exports[`datatype > 1337 > json 1`] = `"{\\"foo\\":\\"U/4:SK$>6Q\\",\\"bar\\":2359372120326144,\\"bike\\":\\"{:e=+kD)[B\\",\\"a\\":\\"e|/Jqjjj!B\\",\\"b\\":\\"GDWQgC2M;q\\",\\"name\\":3648103756333056,\\"prop\\":\\"I1.Gm3tRwn\\"}"`;

exports[`datatype > 1337 > number > noArgs 1`] = `26202`;

Expand Down
4 changes: 2 additions & 2 deletions test/__snapshots__/finance.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ exports[`finance > 42 > currencySymbol 1`] = `"₱"`;

exports[`finance > 42 > ethereumAddress 1`] = `"0x8be4abdd39321ad7d3fe01ffce404f4d6db0906b"`;

exports[`finance > 42 > iban > noArgs 1`] = `"GT30Y75110867098F1E3542612J4"`;
exports[`finance > 42 > iban > noArgs 1`] = `"GT03975110867098F1E3542612J4"`;

exports[`finance > 42 > iban > with formatted 1`] = `"GT30 Y751 1086 7098 F1E3 5426 12J4"`;
exports[`finance > 42 > iban > with formatted 1`] = `"GT03 9751 1086 7098 F1E3 5426 12J4"`;

exports[`finance > 42 > iban > with formatted and countryCode 1`] = `"DE47 7175 0020 0086 0600 97"`;

Expand Down
36 changes: 6 additions & 30 deletions test/__snapshots__/mersenne.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -1,37 +1,13 @@
// Vitest Snapshot v1

exports[`mersenne twister > seed: [42,1,2] > should return deterministic values for next({ min: -50, max: 60 }) 1`] = `44`;
exports[`mersenne twister > seed: [42,1,2] > should return deterministic value for next() 1`] = `0.8562037434894592`;

exports[`mersenne twister > seed: [42,1,2] > should return deterministic values for next({ min: -60, max: 0 }) 1`] = `-9`;
exports[`mersenne twister > seed: [1211,1,2] > should return deterministic value for next() 1`] = `0.8916433283593506`;

exports[`mersenne twister > seed: [42,1,2] > should return deterministic values for next({ min: 0, max: 100 }) 1`] = `85`;
exports[`mersenne twister > seed: [1337,1,2] > should return deterministic value for next() 1`] = `0.17990487208589911`;

exports[`mersenne twister > seed: [1211,1,2] > should return deterministic values for next({ min: -50, max: 60 }) 1`] = `48`;
exports[`mersenne twister > seed: 42 > should return deterministic value for next() 1`] = `0.37454011430963874`;

exports[`mersenne twister > seed: [1211,1,2] > should return deterministic values for next({ min: -60, max: 0 }) 1`] = `-7`;
exports[`mersenne twister > seed: 1211 > should return deterministic value for next() 1`] = `0.9285201537422836`;

exports[`mersenne twister > seed: [1211,1,2] > should return deterministic values for next({ min: 0, max: 100 }) 1`] = `89`;

exports[`mersenne twister > seed: [1337,1,2] > should return deterministic values for next({ min: -50, max: 60 }) 1`] = `-31`;

exports[`mersenne twister > seed: [1337,1,2] > should return deterministic values for next({ min: -60, max: 0 }) 1`] = `-50`;

exports[`mersenne twister > seed: [1337,1,2] > should return deterministic values for next({ min: 0, max: 100 }) 1`] = `17`;

exports[`mersenne twister > seed: 42 > should return deterministic values for next({ min: -50, max: 60 }) 1`] = `-9`;

exports[`mersenne twister > seed: 42 > should return deterministic values for next({ min: -60, max: 0 }) 1`] = `-38`;

exports[`mersenne twister > seed: 42 > should return deterministic values for next({ min: 0, max: 100 }) 1`] = `37`;

exports[`mersenne twister > seed: 1211 > should return deterministic values for next({ min: -50, max: 60 }) 1`] = `52`;

exports[`mersenne twister > seed: 1211 > should return deterministic values for next({ min: -60, max: 0 }) 1`] = `-5`;

exports[`mersenne twister > seed: 1211 > should return deterministic values for next({ min: 0, max: 100 }) 1`] = `92`;

exports[`mersenne twister > seed: 1337 > should return deterministic values for next({ min: -50, max: 60 }) 1`] = `-22`;

exports[`mersenne twister > seed: 1337 > should return deterministic values for next({ min: -60, max: 0 }) 1`] = `-45`;

exports[`mersenne twister > seed: 1337 > should return deterministic values for next({ min: 0, max: 100 }) 1`] = `26`;
exports[`mersenne twister > seed: 1337 > should return deterministic value for next() 1`] = `0.2620246761944145`;
24 changes: 12 additions & 12 deletions test/__snapshots__/number.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ exports[`number > 42 > binary > with options 1`] = `"100"`;

exports[`number > 42 > binary > with value 1`] = `"0"`;

exports[`number > 42 > float > with max 1`] = `25.84`;
exports[`number > 42 > float > with max 1`] = `25.843267887365073`;

exports[`number > 42 > float > with min 1`] = `-25.9`;
exports[`number > 42 > float > with min 1`] = `-25.894775084685534`;

exports[`number > 42 > float > with min and max 1`] = `-0.43`;
exports[`number > 42 > float > with min and max 1`] = `-0.4260473116301`;

exports[`number > 42 > float > with min, max and precision 1`] = `-0.4261`;

exports[`number > 42 > float > with plain number 1`] = `1.5`;
exports[`number > 42 > float > with plain number 1`] = `1.498160457238555`;

exports[`number > 42 > hex > noArgs 1`] = `"5"`;

Expand Down Expand Up @@ -68,15 +68,15 @@ exports[`number > 1211 > binary > with options 1`] = `"1010"`;

exports[`number > 1211 > binary > with value 1`] = `"1"`;

exports[`number > 1211 > float > with max 1`] = `64.07`;
exports[`number > 1211 > float > with max 1`] = `64.06789060821757`;

exports[`number > 1211 > float > with min 1`] = `-2.07`;
exports[`number > 1211 > float > with min 1`] = `-2.073633389081806`;

exports[`number > 1211 > float > with min and max 1`] = `61.07`;
exports[`number > 1211 > float > with min and max 1`] = `61.06573706539348`;

exports[`number > 1211 > float > with min, max and precision 1`] = `61.0658`;

exports[`number > 1211 > float > with plain number 1`] = `3.72`;
exports[`number > 1211 > float > with plain number 1`] = `3.7140806149691343`;

exports[`number > 1211 > hex > noArgs 1`] = `"e"`;

Expand Down Expand Up @@ -116,15 +116,15 @@ exports[`number > 1337 > binary > with options 1`] = `"10"`;

exports[`number > 1337 > binary > with value 1`] = `"0"`;

exports[`number > 1337 > float > with max 1`] = `18.08`;
exports[`number > 1337 > float > with max 1`] = `18.0797026574146`;

exports[`number > 1337 > float > with min 1`] = `-30.74`;
exports[`number > 1337 > float > with min 1`] = `-30.732938923640177`;

exports[`number > 1337 > float > with min and max 1`] = `-12.92`;
exports[`number > 1337 > float > with min and max 1`] = `-12.915260942419991`;

exports[`number > 1337 > float > with min, max and precision 1`] = `-12.9153`;

exports[`number > 1337 > float > with plain number 1`] = `1.05`;
exports[`number > 1337 > float > with plain number 1`] = `1.048098704777658`;

exports[`number > 1337 > hex > noArgs 1`] = `"4"`;

Expand Down
26 changes: 6 additions & 20 deletions test/mersenne.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@ import type { Mersenne } from '../src/internal/mersenne/mersenne';
import mersenneFn from '../src/internal/mersenne/mersenne';
import { seededRuns } from './support/seededRuns';

const minMaxTestCases = [
{ min: 0, max: 100 },
{ min: -60, max: 0 },
{ min: -50, max: 60 },
];

const NON_SEEDED_BASED_RUN = 25;

describe('mersenne twister', () => {
Expand All @@ -24,18 +18,10 @@ describe('mersenne twister', () => {
mersenne.seed(seed);
});

for (const { min, max } of minMaxTestCases) {
it(`should return deterministic values for next({ min: ${min}, max: ${max} })`, () => {
const actual = mersenne.next({ min, max });

expect(actual).toMatchSnapshot();
});
}

it.todo(`should return 0 for next({ min: ${0}, max: ${1} })`, () => {
const actual = mersenne.next({ min: 0, max: 1 });
it(`should return deterministic value for next()`, () => {
const actual = mersenne.next();

expect(actual).toEqual(0);
expect(actual).toMatchSnapshot();
});
});
}
Expand All @@ -57,11 +43,11 @@ describe('mersenne twister', () => {

for (let i = 1; i <= NON_SEEDED_BASED_RUN; i++) {
describe('next', () => {
it('should return random number from interval [min, max)', () => {
const actual = mersenne.next({ min: 0, max: 2 });
it('should return random number from interval [0, 1)', () => {
const actual = mersenne.next();

expect(actual).toBeGreaterThanOrEqual(0);
expect(actual).toBeLessThan(2);
expect(actual).toBeLessThan(1);
});
});
}
Expand Down
17 changes: 12 additions & 5 deletions test/number.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,10 @@ describe('number', () => {
});

describe('float', () => {
it('should return a random float with a default precision of 2 digits after floating point', () => {
it('should return a random float', () => {
const actual = faker.number.float();
expect(actual).toBe(Number(actual.toFixed(2)));
expect(actual).toBeGreaterThanOrEqual(0);
expect(actual).toBeLessThanOrEqual(1);
});

it('should return a random float with given max', () => {
Expand Down Expand Up @@ -220,10 +221,16 @@ describe('number', () => {
}
});

it('provides number with a precision 0', () => {
const actual = faker.number.float({ precision: 0 });
it('throws an error for precision 0', () => {
xDivisionByZerox marked this conversation as resolved.
Show resolved Hide resolved
expect(() => faker.number.float({ precision: 0 })).toThrowError(
new FakerError('Precision should be greater than 0.')
);
});

expect(actual).toBe(Math.floor(actual));
it('throws an error for negative precision', () => {
expect(() => faker.number.float({ precision: -0.01 })).toThrowError(
new FakerError('Precision should be greater than 0.')
);
});

it('should not modify the input object', () => {
Expand Down