Quick Overview

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.

Check Whether a String Is a Signed Decimal Number Without Exponent Notation

Company: LinkedIn

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: medium

Interview Round: Technical Screen

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: 1. An optional single sign character, `+` or `-`. 2. One or more decimal digits `0` through `9`. 3. 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_test` contains 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.

Overview: 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.

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: 1. An optional single sign character, `+` or `-`. 2. One or more decimal digits `0` through `9`. 3. 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++.

Constraints

  • 0 <= len(to_test) <= 100.
  • to_test contains only printable ASCII characters (codes 32 through 126), including spaces.
  • Valid strings use only the characters '+', '-', '.', and the decimal digits '0' through '9'; any other character makes the string invalid rather than being an error.
  • The function returns a boolean and performs no input/output.

Examples

Input: ('',)

Expected Output: False

Explanation: The empty string has no digits, and at least one digit is required.

Input: ('7',)

Expected Output: True

Explanation: A single digit with no sign and no fractional part is the smallest valid number.

Hints

  1. 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.
  2. 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.
  3. 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.

Loading coding console...

Show the approach

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.

  1. 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.
  2. Integer part: remember start = i and 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'.
  3. If the cursor has reached the end of the string, the string is exactly an optional sign plus digits, which is valid: return True.
  4. 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 = i and advance over digits. If no fractional digit was consumed, return False, which rejects '5.'.
  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)