Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit a24777a

Browse files
committed
fix(input): fix step validation for input[type=number/range]
Previously, the validation would incorrectly fail in certain cases (e.g. `step: 0.01`, `value: 1.16 or 20.1`), due to Floating Point Arithmetic limitations. The previous fix for FPA limitations (081d06f) tried to solve the issue by converting the numbers to integers, before doing the actual calculation, but it failed to account for cases where the conversion itself returned non-integer values (again due to FPA limitations). This commit fixes it by ensuring that the values used in the final calculation are always integers. Fixes #15504 Closes #15506
1 parent 99a3adb commit a24777a

File tree

2 files changed

+31
-3
lines changed

2 files changed

+31
-3
lines changed

src/ng/directive/input.js

+14-2
Original file line numberDiff line numberDiff line change
@@ -1565,15 +1565,27 @@ function isValidForStep(viewValue, stepBase, step) {
15651565
// and `viewValue` is expected to be a valid stringified number.
15661566
var value = Number(viewValue);
15671567

1568+
var isNonIntegerValue = !isNumberInteger(value);
1569+
var isNonIntegerStepBase = !isNumberInteger(stepBase);
1570+
var isNonIntegerStep = !isNumberInteger(step);
1571+
15681572
// Due to limitations in Floating Point Arithmetic (e.g. `0.3 - 0.2 !== 0.1` or
15691573
// `0.5 % 0.1 !== 0`), we need to convert all numbers to integers.
1570-
if (!isNumberInteger(value) || !isNumberInteger(stepBase) || !isNumberInteger(step)) {
1571-
var decimalCount = Math.max(countDecimals(value), countDecimals(stepBase), countDecimals(step));
1574+
if (isNonIntegerValue || isNonIntegerStepBase || isNonIntegerStep) {
1575+
var valueDecimals = isNonIntegerValue ? countDecimals(value) : 0;
1576+
var stepBaseDecimals = isNonIntegerStepBase ? countDecimals(stepBase) : 0;
1577+
var stepDecimals = isNonIntegerStep ? countDecimals(step) : 0;
1578+
1579+
var decimalCount = Math.max(valueDecimals, stepBaseDecimals, stepDecimals);
15721580
var multiplier = Math.pow(10, decimalCount);
15731581

15741582
value = value * multiplier;
15751583
stepBase = stepBase * multiplier;
15761584
step = step * multiplier;
1585+
1586+
if (isNonIntegerValue) value = Math.round(value);
1587+
if (isNonIntegerStepBase) stepBase = Math.round(stepBase);
1588+
if (isNonIntegerStep) step = Math.round(step);
15771589
}
15781590

15791591
return (value - stepBase) % step === 0;

test/ng/directive/inputSpec.js

+17-1
Original file line numberDiff line numberDiff line change
@@ -2787,6 +2787,13 @@ describe('input', function() {
27872787
helper.changeInputValueTo('3.5');
27882788
expect(inputElm).toBeValid();
27892789
expect($rootScope.value).toBe(3.5);
2790+
2791+
// 1.16 % 0.01 === 0.009999999999999896
2792+
// 1.16 * 100 === 115.99999999999999
2793+
$rootScope.step = 0.01;
2794+
helper.changeInputValueTo('1.16');
2795+
expect(inputElm).toBeValid();
2796+
expect($rootScope.value).toBe(1.16);
27902797
}
27912798
);
27922799
});
@@ -3656,7 +3663,9 @@ describe('input', function() {
36563663

36573664
it('should correctly validate even in cases where the JS floating point arithmetic fails',
36583665
function() {
3659-
var inputElm = helper.compileInput('<input type="range" ng-model="value" step="0.1" />');
3666+
$rootScope.step = 0.1;
3667+
var inputElm = helper.compileInput(
3668+
'<input type="range" ng-model="value" step="{{step}}" />');
36603669
var ngModel = inputElm.controller('ngModel');
36613670

36623671
expect(inputElm.val()).toBe('');
@@ -3681,6 +3690,13 @@ describe('input', function() {
36813690
helper.changeInputValueTo('3.5');
36823691
expect(inputElm).toBeValid();
36833692
expect($rootScope.value).toBe(3.5);
3693+
3694+
// 1.16 % 0.01 === 0.009999999999999896
3695+
// 1.16 * 100 === 115.99999999999999
3696+
$rootScope.step = 0.01;
3697+
helper.changeInputValueTo('1.16');
3698+
expect(inputElm).toBeValid();
3699+
expect($rootScope.value).toBe(1.16);
36843700
}
36853701
);
36863702
}

0 commit comments

Comments
 (0)