]>
git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/no-loss-of-precision.js
6dc6d864dcdf9931632af7324f55cf8cc79a9f93
2 * @fileoverview Rule to flag numbers that will lose significant figure precision at runtime
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
12 /** @type {import('../shared/types').Rule} */
18 description
: "Disallow literal numbers that lose precision",
20 url
: "https://eslint.org/docs/rules/no-loss-of-precision"
24 noLossOfPrecision
: "This number literal will lose precision at runtime."
31 * Returns whether the node is number literal
32 * @param {Node} node the node literal being evaluated
33 * @returns {boolean} true if the node is a number literal
35 function isNumber(node
) {
36 return typeof node
.value
=== "number";
40 * Gets the source code of the given number literal. Removes `_` numeric separators from the result.
41 * @param {Node} node the number `Literal` node
42 * @returns {string} raw source code of the literal, without numeric separators
44 function getRaw(node
) {
45 return node
.raw
.replace(/_
/gu
, "");
49 * Checks whether the number is base ten
50 * @param {ASTNode} node the node being evaluated
51 * @returns {boolean} true if the node is in base ten
53 function isBaseTen(node
) {
54 const prefixes
= ["0x", "0X", "0b", "0B", "0o", "0O"];
56 return prefixes
.every(prefix
=> !node
.raw
.startsWith(prefix
)) &&
57 !/^0[0-7]+$/u.test(node
.raw
);
61 * Checks that the user-intended non-base ten number equals the actual number after is has been converted to the Number type
62 * @param {Node} node the node being evaluated
63 * @returns {boolean} true if they do not match
65 function notBaseTenLosesPrecision(node
) {
66 const rawString
= getRaw(node
).toUpperCase();
69 if (rawString
.startsWith("0B")) {
71 } else if (rawString
.startsWith("0X")) {
77 return !rawString
.endsWith(node
.value
.toString(base
).toUpperCase());
81 * Adds a decimal point to the numeric string at index 1
82 * @param {string} stringNumber the numeric string without any decimal point
83 * @returns {string} the numeric string with a decimal point in the proper place
85 function addDecimalPointToNumber(stringNumber
) {
86 return `${stringNumber.slice(0, 1)}.${stringNumber.slice(1)}`;
90 * Returns the number stripped of leading zeros
91 * @param {string} numberAsString the string representation of the number
92 * @returns {string} the stripped string
94 function removeLeadingZeros(numberAsString
) {
95 return numberAsString
.replace(/^0*/u, "");
99 * Returns the number stripped of trailing zeros
100 * @param {string} numberAsString the string representation of the number
101 * @returns {string} the stripped string
103 function removeTrailingZeros(numberAsString
) {
104 return numberAsString
.replace(/0*$/u, "");
108 * Converts an integer to to an object containing the integer's coefficient and order of magnitude
109 * @param {string} stringInteger the string representation of the integer being converted
110 * @returns {Object} the object containing the integer's coefficient and order of magnitude
112 function normalizeInteger(stringInteger
) {
113 const significantDigits
= removeTrailingZeros(removeLeadingZeros(stringInteger
));
116 magnitude
: stringInteger
.startsWith("0") ? stringInteger
.length
- 2 : stringInteger
.length
- 1,
117 coefficient
: addDecimalPointToNumber(significantDigits
)
123 * Converts a float to to an object containing the floats's coefficient and order of magnitude
124 * @param {string} stringFloat the string representation of the float being converted
125 * @returns {Object} the object containing the integer's coefficient and order of magnitude
127 function normalizeFloat(stringFloat
) {
128 const trimmedFloat
= removeLeadingZeros(stringFloat
);
130 if (trimmedFloat
.startsWith(".")) {
131 const decimalDigits
= trimmedFloat
.split(".").pop();
132 const significantDigits
= removeLeadingZeros(decimalDigits
);
135 magnitude
: significantDigits
.length
- decimalDigits
.length
- 1,
136 coefficient
: addDecimalPointToNumber(significantDigits
)
141 magnitude
: trimmedFloat
.indexOf(".") - 1,
142 coefficient
: addDecimalPointToNumber(trimmedFloat
.replace(".", ""))
149 * Converts a base ten number to proper scientific notation
150 * @param {string} stringNumber the string representation of the base ten number to be converted
151 * @returns {string} the number converted to scientific notation
153 function convertNumberToScientificNotation(stringNumber
) {
154 const splitNumber
= stringNumber
.replace("E", "e").split("e");
155 const originalCoefficient
= splitNumber
[0];
156 const normalizedNumber
= stringNumber
.includes(".") ? normalizeFloat(originalCoefficient
)
157 : normalizeInteger(originalCoefficient
);
158 const normalizedCoefficient
= normalizedNumber
.coefficient
;
159 const magnitude
= splitNumber
.length
> 1 ? (parseInt(splitNumber
[1], 10) + normalizedNumber
.magnitude
)
160 : normalizedNumber
.magnitude
;
162 return `${normalizedCoefficient}e${magnitude}`;
167 * Checks that the user-intended base ten number equals the actual number after is has been converted to the Number type
168 * @param {Node} node the node being evaluated
169 * @returns {boolean} true if they do not match
171 function baseTenLosesPrecision(node
) {
172 const normalizedRawNumber
= convertNumberToScientificNotation(getRaw(node
));
173 const requestedPrecision
= normalizedRawNumber
.split("e")[0].replace(".", "").length
;
175 if (requestedPrecision
> 100) {
178 const storedNumber
= node
.value
.toPrecision(requestedPrecision
);
179 const normalizedStoredNumber
= convertNumberToScientificNotation(storedNumber
);
181 return normalizedRawNumber
!== normalizedStoredNumber
;
186 * Checks that the user-intended number equals the actual number after is has been converted to the Number type
187 * @param {Node} node the node being evaluated
188 * @returns {boolean} true if they do not match
190 function losesPrecision(node
) {
191 return isBaseTen(node
) ? baseTenLosesPrecision(node
) : notBaseTenLosesPrecision(node
);
197 if (node
.value
&& isNumber(node
) && losesPrecision(node
)) {
199 messageId
: "noLossOfPrecision",