Skip to content

Commit

Permalink
✨ Add constraints-based signature for float and double (#1068)
Browse files Browse the repository at this point in the history
  • Loading branch information
dubzzz authored Oct 18, 2020
1 parent 5a565ec commit 51f5795
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 54 deletions.
11 changes: 11 additions & 0 deletions codemods/unify-signatures/snippet-5.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,14 @@ fc.bigUint();
fc.bigUint(3n);
fc.bigUint(BigInt(3));
fc.bigUint({ max: 3n });

// float

fc.float();
fc.float(1.0);
fc.float(2.0);
fc.float(0.0, 1.0);
fc.float(0.0, 2.0);
fc.float(-1.0, 1.0);
fc.float(-1.0, 2.0);
fc.float({ min: -1.0, max: 2.0 });
23 changes: 23 additions & 0 deletions codemods/unify-signatures/transform.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,29 @@ module.exports = function (file, api, options) {
}
break;
}
case 'double':
case 'float': {
if (p.value.arguments.length === 1 && p.value.arguments[0].type !== 'ObjectExpression') {
// fc.float(max) -> fc.float({max})
const simplifyMax = options.simplifyMax && isNumericValue(p.value.arguments[0], 1.0);
p.value.arguments = computeNewArguments(
[],
[!simplifyMax && j.property('init', j.identifier('max'), p.value.arguments[0])]
);
} else if (p.value.arguments.length === 2) {
// fc.float(min, max) -> fc.float({min, max})
const simplifyMin = options.simplifyMin && isNumericValue(p.value.arguments[0], 0.0);
const simplifyMax = options.simplifyMax && isNumericValue(p.value.arguments[1], 1.0);
p.value.arguments = computeNewArguments(
[],
[
!simplifyMin && j.property('init', j.identifier('min'), p.value.arguments[0]),
!simplifyMax && j.property('init', j.identifier('max'), p.value.arguments[1]),
]
);
}
break;
}
}
return p;
})
Expand Down
34 changes: 18 additions & 16 deletions documentation/Arbitraries.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,13 +202,14 @@ fc.maxSafeNat()
* Signatures*

- `fc.float()`
- `fc.float(maxValue)`
- `fc.float(minValue, maxValue)`
- `fc.float({min?, max?})`
- `fc.float(min, max)`
- _`fc.float(max)`_ — _not recommended ([#992](https://github.com/dubzzz/fast-check/issues/992))_

* with:*

- `minValue?` — default: `0.0`_lower bound of the range (included)_
- `maxValue?` — default: `1.0`_upper bound of the range (excluded)_
- `min?` — default: `0.0`_lower bound of the range (included)_
- `max?` — default: `1.0`_upper bound of the range (excluded)_

* Usages*

Expand All @@ -217,13 +218,13 @@ fc.float()
// Note: All possible 32-bit floating point values between `0.0` (included) and `1.0` (excluded)
// Examples of generated values: 0.731347382068634, 1.1920928955078125e-7, 0.6597227454185486, 0.5946863293647766, 0.6302104592323303…

fc.float(100)
fc.float({max: 100})
// Note: All possible 32-bit floating point values between `0.0` (included) and `100.0` (excluded)
// Examples of generated values: 0.00007748603820800781, 0.00007152557373046875, 0.00013113021850585938, 52.37404108047485, 0.000035762786865234375
// Examples of generated values: 0.00004172325134277344, 0.000011920928955078125, 0.0001251697540283203, 60.498785972595215, 0.000029802322387695312

fc.float(-100, 100)
fc.float({min: -100, max: 100})
// Note: All possible 32-bit floating point values between `-100.0` (included) and `100.0` (excluded)
// Examples of generated values: -99.99992847442627, 55.83081245422363, -99.99979734420776, -20.923829078674316, -99.99991655349731
// Examples of generated values: -99.9997615814209, -99.99988079071045, 81.314218044281, -99.99974966049194, -99.99978542327881
```
</details>

Expand All @@ -240,13 +241,14 @@ fc.float(-100, 100)
*&#8195;Signatures*

- `fc.double()`
- `fc.double(maxValue)`
- `fc.double(minValue, maxValue)`
- `fc.double({min?, max?})`
- `fc.double(min, max)`
- _`fc.double(max)`_ — _not recommended ([#992](https://github.com/dubzzz/fast-check/issues/992))_

*&#8195;with:*

- `minValue?` — default: `0.0`_lower bound of the range (included)_
- `maxValue?` — default: `1.0`_upper bound of the range (excluded)_
- `min?` — default: `0.0`_lower bound of the range (included)_
- `max?` — default: `1.0`_upper bound of the range (excluded)_

*&#8195;Usages*

Expand All @@ -255,13 +257,13 @@ fc.double()
// Note: All possible floating point values between `0.0` (included) and `1.0` (excluded)
// Examples of generated values: 0.4530413804731288, 0.8226463198661805, 0.3829372459587349, 0.7186836451292051, 0.8065718412399292…

fc.double(100)
fc.double({max: 100})
// Note: All possible floating point values between `0.0` (included) and `100.0` (excluded)
// Examples of generated values: 0.000019014520535876045, 98.91013210040657, 0.00003648309874204614, 20.497548580169944, 0.00001937150981845548
// Examples of generated values: 78.93341034650808, 0.000034884977251348204, 98.5225079632713, 10.198144676861675, 0.000008702743647948097

fc.double(-100, 100)
fc.double({min: -100, max: 100})
// Note: All possible floating point values between `-100.0` (included) and `100.0` (excluded)
// Examples of generated values: -99.999970715887, -99.99996384938794, -99.99996463982544, -69.75060565839972, -99.99994324436676
// Examples of generated values: -99.99994117592425, 37.9800438880923, 1.4435261487962805, -99.9999970197672, -99.99993741512299
```
</details>

Expand Down
84 changes: 76 additions & 8 deletions src/check/arbitrary/FloatingPointArbitrary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,17 @@ const floatInternal = (): Arbitrary<number> => {
return next(24).map((v) => v / (1 << 24));
};

/**
* Constraints to be applied on {@link float}
* @public
*/
export interface FloatConstraints {
/** Lower bound for the generated floats (included) */
min?: number;
/** Upper bound for the generated floats (excluded) */
max?: number;
}

/**
* For floating point numbers between 0.0 (included) and 1.0 (excluded) - accuracy of `1 / 2**24`
* @public
Expand All @@ -23,6 +34,10 @@ function float(): Arbitrary<number>;
*
* @param max - Upper bound of the generated floating point
*
* @remarks
* Superceded by `fc.float({max})` - see {@link https://github.com/dubzzz/fast-check/issues/992 | #992}.
* Ease the migration with {@link https://github.com/dubzzz/fast-check/tree/master/codemods/unify-signatures | our codemod script}.
*
* @public
*/
function float(max: number): Arbitrary<number>;
Expand All @@ -32,13 +47,32 @@ function float(max: number): Arbitrary<number>;
* @param min - Lower bound of the generated floating point
* @param max - Upper bound of the generated floating point
*
* @remarks
* You may prefer to use `fc.float({min, max})` instead.
*
* @public
*/
function float(min: number, max: number): Arbitrary<number>;
function float(a?: number, b?: number): Arbitrary<number> {
if (a === undefined) return floatInternal();
if (b === undefined) return floatInternal().map((v) => v * a);
return floatInternal().map((v) => a + v * (b - a));
/**
* For floating point numbers in range defined by constraints - accuracy of `(max - min) / 2**24`
*
* @param constraints - Constraints to apply when building instances
*
* @public
*/
function float(constraints: FloatConstraints): Arbitrary<number>;
function float(...args: [] | [number] | [number, number] | [FloatConstraints]): Arbitrary<number> {
if (typeof args[0] === 'object') {
const min = args[0].min !== undefined ? args[0].min : 0;
const max = args[0].max !== undefined ? args[0].max : 1;
return floatInternal().map((v) => min + v * (max - min));
} else {
const a = args[0];
const b = args[1];
if (a === undefined) return floatInternal();
if (b === undefined) return floatInternal().map((v) => v * a);
return floatInternal().map((v) => a + v * (b - a));
}
}

/** @internal */ const doubleFactor = Math.pow(2, 27);
Expand All @@ -50,6 +84,17 @@ const doubleInternal = (): Arbitrary<number> => {
return tuple(next(26), next(27)).map((v) => (v[0] * doubleFactor + v[1]) * doubleDivisor);
};

/**
* Constraints to be applied on {@link double}
* @public
*/
export interface DoubleConstraints {
/** Lower bound for the generated doubles (included) */
min?: number;
/** Upper bound for the generated doubles (excluded) */
max?: number;
}

/**
* For floating point numbers between 0.0 (included) and 1.0 (excluded) - accuracy of `1 / 2**53`
* @public
Expand All @@ -60,6 +105,10 @@ function double(): Arbitrary<number>;
*
* @param max - Upper bound of the generated floating point
*
* @remarks
* Superceded by `fc.double({max})` - see {@link https://github.com/dubzzz/fast-check/issues/992 | #992}.
* Ease the migration with {@link https://github.com/dubzzz/fast-check/tree/master/codemods/unify-signatures | our codemod script}.
*
* @public
*/
function double(max: number): Arbitrary<number>;
Expand All @@ -69,13 +118,32 @@ function double(max: number): Arbitrary<number>;
* @param min - Lower bound of the generated floating point
* @param max - Upper bound of the generated floating point
*
* @remarks
* You may prefer to use `fc.double({min, max})` instead.
*
* @public
*/
function double(min: number, max: number): Arbitrary<number>;
function double(a?: number, b?: number): Arbitrary<number> {
if (a === undefined) return doubleInternal();
if (b === undefined) return doubleInternal().map((v) => v * a);
return doubleInternal().map((v) => a + v * (b - a));
/**
* For floating point numbers in range defined by constraints - accuracy of `(max - min) / 2**53`
*
* @param constraints - Constraints to apply when building instances
*
* @public
*/
function double(constraints: DoubleConstraints): Arbitrary<number>;
function double(...args: [] | [number] | [number, number] | [DoubleConstraints]): Arbitrary<number> {
if (typeof args[0] === 'object') {
const min = args[0].min !== undefined ? args[0].min : 0;
const max = args[0].max !== undefined ? args[0].max : 1;
return doubleInternal().map((v) => min + v * (max - min));
} else {
const a = args[0];
const b = args[1];
if (a === undefined) return doubleInternal();
if (b === undefined) return doubleInternal().map((v) => v * a);
return doubleInternal().map((v) => a + v * (b - a));
}
}

export { float, double };
4 changes: 3 additions & 1 deletion src/fast-check-default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import { Arbitrary } from './check/arbitrary/definition/Arbitrary';
import { Shrinkable } from './check/arbitrary/definition/Shrinkable';
import { dictionary } from './check/arbitrary/DictionaryArbitrary';
import { emailAddress } from './check/arbitrary/EmailArbitrary';
import { double, float } from './check/arbitrary/FloatingPointArbitrary';
import { double, float, DoubleConstraints, FloatConstraints } from './check/arbitrary/FloatingPointArbitrary';
import { frequency, WeightedArbitrary, FrequencyValue } from './check/arbitrary/FrequencyArbitrary';
import { compareBooleanFunc, compareFunc, func } from './check/arbitrary/FunctionArbitrary';
import { domain } from './check/arbitrary/HostArbitrary';
Expand Down Expand Up @@ -291,7 +291,9 @@ export {
BigIntConstraints,
BigUintConstraints,
CommandsContraints,
DoubleConstraints,
FalsyContraints,
FloatConstraints,
JsonSharedConstraints,
LoremConstraints,
MixedCaseConstraints,
Expand Down
Loading

0 comments on commit 51f5795

Please sign in to comment.