Coding questions · LinkedIn
Check Whether a String Is a Signed Decimal Number Without Exponent Notation
Write a function that decides whether a string is a valid plain decimal number with an optional sign and fractional part, rejecting exponents, stray symbols, and repeated decimal points. It tests turning a small grammar into careful character-by-character validation and covering edge cases such as empty strings and bare signs.
Implement a function that decides whether a string is a valid number in plain decimal notation: an optional sign, digits, and an optional fractional part. Exponent notation and any other symbols are not allowed.
Function Signature
is_number(to_test: str) -> bool
Rules
A string is valid exactly when the whole string matches this form, with nothing before or after it:
- An optional single sign character,
+or-. - One or more decimal digits
0through9. - Optionally, a single
.followed by one or more decimal digits.
Consequences of the rules:
"234","-455", and"23.23"are valid."1e2"is invalid because exponent notation is not supported, and"12^2"is invalid because^is not allowed."2.2.4"is invalid because at most one decimal point may appear.- Leading zeros are allowed, so
"007"and"-0"are valid. "","-",".5","5.", and any string containing whitespace are invalid.
Accepting +, allowing leading zeros, and requiring digits on both sides of a decimal point are conventions of this exercise.
Constraints
0 <= len(to_test) <= 100.to_testcontains only printable ASCII characters, including spaces.
Examples
Input: to_test = "-455"
Output: True
Input: to_test = "23.23"
Output: True
Input: to_test = "2.2.4"
Output: False
The second decimal point makes the string invalid.
Validate a Plain Decimal Number String
Given a string to_test, decide whether the entire string is a valid number written in plain decimal notation: an optional sign, digits, and an optional fractional part. Exponent notation and any other symbols are not allowed.
Return True exactly when the whole string, with nothing before it and nothing after it, matches this form:
- An optional single sign character,
+or-. - One or more decimal digits
0through9. - Optionally, a single
.followed by one or more decimal digits.
Otherwise return False.
Consequences of the rules:
"234","-455", and"23.23"are valid."1e2"is invalid because exponent notation is not supported, and"12^2"is invalid because^is not allowed."2.2.4"is invalid because at most one decimal point may appear.- Leading zeros are allowed, so
"007"and"-0"are valid. "","-",".5","5.", and any string containing whitespace are invalid.
Accepting +, allowing leading zeros, and requiring digits on both sides of a decimal point are conventions of this exercise.
Examples
Example 1
Input: to_test = "-455"
Output: True
Explanation: An optional sign followed by one or more digits, with no fractional part, matches the form.
Example 2
Input: to_test = "2.2.4"
Output: False
Explanation: The second decimal point makes the string invalid.
Output semantics
The return value is a single boolean, so there is no ordering or tie-breaking to decide. No numeric value in this problem can exceed 2^31 - 1: only string indices are computed, and the string is at most 100 characters long, so a 32-bit int index is sufficient in Java and C++.
Hints
- The form has exactly three parts in a fixed order: an optional sign, an integer part, and an optional fractional part. Work out where each part ends before deciding anything.
- Both digit runs must be non-empty when their part is present. Ask yourself which of those two requirements each of "", "-", ".5", and "5." violates.
- Validity is about the whole string: anything left over after the form has been matched, whether a second '.', a trailing sign, or a space, makes the answer False.
Approach
Algorithm. The grammar is a fixed sequence of three parts, so a single left-to-right scan with one cursor i decides membership without backtracking.
- Sign: if the string is non-empty and the first character is '+' or '-', advance the cursor by one. At most one sign character can ever be consumed, so '+-5' and '--5' fail later when the integer part turns out to be empty.
- Integer part: remember
start = iand advance while the current character is a decimal digit. If no character was consumed (i == start), the required 'one or more digits' is missing and the answer is False. This single check rejects '', '-', '+', '.', '.5' and '-.5'. - If the cursor has reached the end of the string, the string is exactly an optional sign plus digits, which is valid: return True.
- Fractional part: the only character allowed to follow the integer part is '.'; anything else (a second sign, 'e', '^', ',', a space) means the string does not match as a whole, so return False. After consuming the '.', remember
frac_start = iand advance over digits. If no fractional digit was consumed, return False, which rejects '5.'. - Finally return
i == n. Any leftover character, such as the second '.' in '2.2.4' or a trailing space, makes this False.
Invariant. At every step the prefix to_test[0:i] is exactly the portion of the string already matched against the grammar prefix processed so far, and i never moves backwards. Because each grammar part is greedy over a character class that is disjoint from the character that may start the next part (digits vs. '.'), greedy consumption never discards a match that a shorter consumption would have found: if a digit run stopped early, the next required character would still have to be '.', which is not a digit, so there is no alternative parse to explore.
Correctness. The routine accepts a string if and only if it can be split as sign? digits+ ('.' digits+)? with no remainder. Each early return corresponds to exactly one clause of the specification being violated: empty integer part (step 2), a non-'.' character where the string should have ended (step 4), an empty fractional part (step 4), and trailing characters (step 5). Conversely, a string that reaches the final return with i == n was consumed entirely by those grammar parts in order, so it matches.
Edge cases. Empty input returns False at step 2 because the loop never runs. A sign with no digits ('-', '+') returns False at the same place. Leading zeros are never special-cased, so '007' and '-0' are accepted as required. The digit test compares characters against '0' and '9' rather than using a locale- or Unicode-aware predicate, so no non-ASCII digit, and no character outside '0'-'9', is ever accepted; the input domain is printable ASCII anyway. Comparison is on raw characters, so whitespace is handled by the generic 'unexpected character' paths rather than by any trimming.
Complexity. Each character is examined a constant number of times and only a few integer cursors are kept.
- Time complexity: O(n)
- Space complexity: O(1)