]>
git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/no-loss-of-precision.js
417616dd231c93d73cfbb69141b818ed3de02e5d
2 * @fileoverview Rule to flag numbers that will lose significant figure precision at runtime
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
17 description
: "disallow literal numbers that lose precision",
19 url
: "https://eslint.org/docs/rules/no-loss-of-precision"
23 noLossOfPrecision
: "This number literal will lose precision at runtime."
30 * Returns whether the node is number literal
31 * @param {Node} node the node literal being evaluated
32 * @returns {boolean} true if the node is a number literal
34 function isNumber(node
) {
35 return typeof node
.value
=== "number";
39 * Gets the source code of the given number literal. Removes `_` numeric separators from the result.
40 * @param {Node} node the number `Literal` node
41 * @returns {string} raw source code of the literal, without numeric separators
43 function getRaw(node
) {
44 return node
.raw
.replace(/_
/gu
, "");
48 * Checks whether the number is base ten
49 * @param {ASTNode} node the node being evaluated
50 * @returns {boolean} true if the node is in base ten
52 function isBaseTen(node
) {
53 const prefixes
= ["0x", "0X", "0b", "0B", "0o", "0O"];
55 return prefixes
.every(prefix
=> !node
.raw
.startsWith(prefix
)) &&
56 !/^0[0-7]+$/u.test(node
.raw
);
60 * Checks that the user-intended non-base ten number equals the actual number after is has been converted to the Number type
61 * @param {Node} node the node being evaluated
62 * @returns {boolean} true if they do not match
64 function notBaseTenLosesPrecision(node
) {
65 const rawString
= getRaw(node
).toUpperCase();
68 if (rawString
.startsWith("0B")) {
70 } else if (rawString
.startsWith("0X")) {
76 return !rawString
.endsWith(node
.value
.toString(base
).toUpperCase());
80 * Adds a decimal point to the numeric string at index 1
81 * @param {string} stringNumber the numeric string without any decimal point
82 * @returns {string} the numeric string with a decimal point in the proper place
84 function addDecimalPointToNumber(stringNumber
) {
85 return `${stringNumber.slice(0, 1)}.${stringNumber.slice(1)}`;
89 * Returns the number stripped of leading zeros
90 * @param {string} numberAsString the string representation of the number
91 * @returns {string} the stripped string
93 function removeLeadingZeros(numberAsString
) {
94 return numberAsString
.replace(/^0*/u, "");
98 * Returns the number stripped of trailing zeros
99 * @param {string} numberAsString the string representation of the number
100 * @returns {string} the stripped string
102 function removeTrailingZeros(numberAsString
) {
103 return numberAsString
.replace(/0*$/u, "");
107 * Converts an integer to to an object containing the integer's coefficient and order of magnitude
108 * @param {string} stringInteger the string representation of the integer being converted
109 * @returns {Object} the object containing the integer's coefficient and order of magnitude
111 function normalizeInteger(stringInteger
) {
112 const significantDigits
= removeTrailingZeros(removeLeadingZeros(stringInteger
));
115 magnitude
: stringInteger
.startsWith("0") ? stringInteger
.length
- 2 : stringInteger
.length
- 1,
116 coefficient
: addDecimalPointToNumber(significantDigits
)
122 * Converts a float to to an object containing the floats's coefficient and order of magnitude
123 * @param {string} stringFloat the string representation of the float being converted
124 * @returns {Object} the object containing the integer's coefficient and order of magnitude
126 function normalizeFloat(stringFloat
) {
127 const trimmedFloat
= removeLeadingZeros(stringFloat
);
129 if (trimmedFloat
.startsWith(".")) {
130 const decimalDigits
= trimmedFloat
.split(".").pop();
131 const significantDigits
= removeLeadingZeros(decimalDigits
);
134 magnitude
: significantDigits
.length
- decimalDigits
.length
- 1,
135 coefficient
: addDecimalPointToNumber(significantDigits
)
140 magnitude
: trimmedFloat
.indexOf(".") - 1,
141 coefficient
: addDecimalPointToNumber(trimmedFloat
.replace(".", ""))
148 * Converts a base ten number to proper scientific notation
149 * @param {string} stringNumber the string representation of the base ten number to be converted
150 * @returns {string} the number converted to scientific notation
152 function convertNumberToScientificNotation(stringNumber
) {
153 const splitNumber
= stringNumber
.replace("E", "e").split("e");
154 const originalCoefficient
= splitNumber
[0];
155 const normalizedNumber
= stringNumber
.includes(".") ? normalizeFloat(originalCoefficient
)
156 : normalizeInteger(originalCoefficient
);
157 const normalizedCoefficient
= normalizedNumber
.coefficient
;
158 const magnitude
= splitNumber
.length
> 1 ? (parseInt(splitNumber
[1], 10) + normalizedNumber
.magnitude
)
159 : normalizedNumber
.magnitude
;
161 return `${normalizedCoefficient}e${magnitude}`;
166 * Checks that the user-intended base ten number equals the actual number after is has been converted to the Number type
167 * @param {Node} node the node being evaluated
168 * @returns {boolean} true if they do not match
170 function baseTenLosesPrecision(node
) {
171 const normalizedRawNumber
= convertNumberToScientificNotation(getRaw(node
));
172 const requestedPrecision
= normalizedRawNumber
.split("e")[0].replace(".", "").length
;
174 if (requestedPrecision
> 100) {
177 const storedNumber
= node
.value
.toPrecision(requestedPrecision
);
178 const normalizedStoredNumber
= convertNumberToScientificNotation(storedNumber
);
180 return normalizedRawNumber
!== normalizedStoredNumber
;
185 * Checks that the user-intended number equals the actual number after is has been converted to the Number type
186 * @param {Node} node the node being evaluated
187 * @returns {boolean} true if they do not match
189 function losesPrecision(node
) {
190 return isBaseTen(node
) ? baseTenLosesPrecision(node
) : notBaseTenLosesPrecision(node
);
196 if (node
.value
&& isNumber(node
) && losesPrecision(node
)) {
198 messageId
: "noLossOfPrecision",