Skip to content

Commit 2111959

Browse files
[format] Add a check for standard scientific notation
1 parent d1ea622 commit 2111959

File tree

3 files changed

+125
-3
lines changed

3 files changed

+125
-3
lines changed

pylint/checkers/format.py

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,11 @@
111111
"unexpected-line-ending-format",
112112
"Used when there is different newline than expected.",
113113
),
114+
"C0329": (
115+
"Scientific notation should be '%s' instead",
116+
"use-standard-scientific-notation",
117+
"Emitted when a number is written in non-standard scientific notation.",
118+
),
114119
}
115120

116121

@@ -372,6 +377,16 @@ def _check_keyword_parentheses(
372377
self._check_keyword_parentheses(tokens[i:], 0)
373378
return
374379

380+
@staticmethod
381+
def to_standard_scientific_notation(number: float) -> str:
382+
# number is always going to be > 0 because node constants are always positive
383+
# Format with high precision to capture all digits
384+
s = f"{number:.15e}"
385+
base, exp = s.split("e")
386+
# Remove trailing zeros and possible trailing decimal point
387+
base = base.rstrip("0").rstrip(".")
388+
return f"{base}e{int(exp)}"
389+
375390
def process_tokens(self, tokens: list[tokenize.TokenInfo]) -> None:
376391
"""Process tokens and search for:
377392
@@ -430,9 +445,23 @@ def process_tokens(self, tokens: list[tokenize.TokenInfo]) -> None:
430445
if check_equal:
431446
check_equal = False
432447
self.check_indent_level(line, indents[-1], line_num)
433-
434-
if tok_type == tokenize.NUMBER and string.endswith("l"):
435-
self.add_message("lowercase-l-suffix", line=line_num)
448+
if tok_type == tokenize.NUMBER:
449+
# Check for wrong scientific notation
450+
if (
451+
("e" in string or "E" in string)
452+
and "x" not in string # not a hexadecimal
453+
and "j" not in string # not a complex
454+
):
455+
value = float(string.lower().split("e")[0])
456+
if not (1 <= value < 10):
457+
self.add_message(
458+
"use-standard-scientific-notation",
459+
args=(self.to_standard_scientific_notation(value)),
460+
line=line_num,
461+
col_offset=start[1],
462+
)
463+
if string.endswith("l"):
464+
self.add_message("lowercase-l-suffix", line=line_num)
436465

437466
if string in _KEYWORD_TOKENS:
438467
self._check_keyword_parentheses(tokens, idx)
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# pylint: disable=missing-docstring,invalid-name
2+
3+
wrong_big = 45.3e6 # [use-standard-scientific-notation]
4+
uppercase_e_wrong = 45.3E6 # [use-standard-scientific-notation]
5+
wrong_small = 0.00012e-26 # [use-standard-scientific-notation]
6+
wrong_negative_and_big = -10e3 # [use-standard-scientific-notation]
7+
actual_trolling = 11000e26 # [use-standard-scientific-notation]
8+
scientific_double_digit = 12e8 # [use-standard-scientific-notation]
9+
scientific_triple_digit = 123e3 # [use-standard-scientific-notation]
10+
zero_before_decimal_small = 0.0001e-5 # [use-standard-scientific-notation]
11+
zero_before_decimal_big = 0.0001e5 # [use-standard-scientific-notation]
12+
negative_decimal = -0.5e10 # [use-standard-scientific-notation]
13+
zero_only = 0e10 # [use-standard-scientific-notation]
14+
15+
one_only = 1e6
16+
correct_1 = 4.53e7
17+
uppercase_e_correct = 4.53E7
18+
uppercase_e_with_plus = 1.2E+10
19+
uppercase_e_with_minus = 5.67E-8
20+
correct_2 = 1.2e-28
21+
correct_3 = -1.0e4
22+
correct_4 = 1.1E30
23+
correct_with_digits = 4.567e8
24+
correct_with_plus = 1.2e+10
25+
correct_decimal_only = 3.14
26+
negative_correct = -5.67e-8
27+
correct_small_exponent = 1.5e1
28+
correct_tiny_exponent = 9.0e0
29+
correct_precise = 6.02214076e23
30+
31+
hex_constant = 0x1e4 # Hexadecimal, not scientific notation
32+
binary_constant = 0b1010
33+
octal_constant = 0o1234
34+
inside_string = "Temperature: 10e3 degrees"
35+
inside_multiline = """
36+
This is a test with 45.3e6 inside
37+
"""
38+
inside_comment = 1.0 # This comment has 12e4 in it
39+
in_variable_name = measurement_10e3 = 45
40+
inside_f_string = f"Value is {1.0} not 10e6"
41+
42+
# Potential false negatives
43+
barely_violation = 9.99e0 # Should this be 9.99?
44+
integer_sci = int(1e10) # Integer call with scientific notation
45+
complex_number = 1.5e3 + 2.5e3j # Complex number with scientific notation
46+
tuple_of_sci = (1.2e4, 3.4e5)
47+
list_of_sci = [5.6e6, 7.8e7]
48+
dict_with_sci = {"a": 9.1e8, "b": 1.2e9}
49+
50+
# Mathematical operations
51+
addition = 1.0e3 + 2.0e3
52+
multiplication = 1.0e3 * 2.0
53+
division = 1.0e3 / 2.0
54+
power = 1.0e3 ** 2.0
55+
56+
# Function calls with scientific notation
57+
def function_with_sci(param=1.0e3, other_param=2.0e3):
58+
return param, other_param
59+
60+
result = function_with_sci(2.0e3)
61+
positional_and_keyword = function_with_sci(1.0, other_param=3.0e4)
62+
63+
# Assignments with operations
64+
a = 1
65+
a += 1.0e3
66+
b = 2
67+
b *= 2.0e3
68+
69+
# Scientific notation in different contexts
70+
inside_list_comp = [x * 2 for x in [1.0e3, 2.0e3]]
71+
inside_dict_comp = {str(x): x for x in [3.0e3, 4.0e3]}
72+
inside_generator = (x + 1 for x in [5.0e3, 6.0e3])
73+
74+
# Boundary cases for normalization
75+
boundary_small = 9.999e0 # Almost 10, but not quite
76+
boundary_large = 1.001e0 # Just above 1
77+
boundary_case = 1.0e0 # Equal to 1
78+
79+
# Constants from physics/science (correctly formatted)
80+
speed_of_light = 2.99792458e8 # m/s
81+
planck_constant = 6.62607015e-34 # J⋅s
82+
electron_charge = 1.602176634e-19 # C
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
use-standard-scientific-notation:3:12:None:None::Scientific notation should be '4.53e1' instead:UNDEFINED
2+
use-standard-scientific-notation:4:20:None:None::Scientific notation should be '4.53e1' instead:UNDEFINED
3+
use-standard-scientific-notation:5:14:None:None::Scientific notation should be '1.2e-4' instead:UNDEFINED
4+
use-standard-scientific-notation:6:26:None:None::Scientific notation should be '1e1' instead:UNDEFINED
5+
use-standard-scientific-notation:7:18:None:None::Scientific notation should be '1.1e4' instead:UNDEFINED
6+
use-standard-scientific-notation:8:26:None:None::Scientific notation should be '1.2e1' instead:UNDEFINED
7+
use-standard-scientific-notation:9:26:None:None::Scientific notation should be '1.23e2' instead:UNDEFINED
8+
use-standard-scientific-notation:10:28:None:None::Scientific notation should be '1e-4' instead:UNDEFINED
9+
use-standard-scientific-notation:11:26:None:None::Scientific notation should be '1e-4' instead:UNDEFINED
10+
use-standard-scientific-notation:12:20:None:None::Scientific notation should be '5e-1' instead:UNDEFINED
11+
use-standard-scientific-notation:13:12:None:None::Scientific notation should be '0e0' instead:UNDEFINED

0 commit comments

Comments
 (0)