Implement string-based rounding without floats
Company: Pinterest
Role: Machine Learning Engineer
Category: Coding & Algorithms
Difficulty: easy
Interview Round: Technical Screen
Quick Answer: This question evaluates proficiency in string-based numeric processing, arbitrary-precision arithmetic concepts, precise rounding rules including deterministic tie-breaking, and handling of sign and decimal edge cases, and falls under the Coding & Algorithms domain.
String-Based round() to Nearest Integer
Constraints
- Do not parse the input with float()/double/Number; operate on the string directly.
- Input matches an optional sign, then digits and/or a single '.', e.g. '+', '-', '.', and 0-9.
- The integer part may be arbitrarily long (longer than any native numeric type).
- Tie-breaking is round half away from zero (first fractional digit >= 5 rounds the magnitude up).
Examples
Input: ("-.2",)
Expected Output: "0"
Explanation: -0.2 rounds toward 0; the sign is dropped because the magnitude is zero.
Input: ("2.",)
Expected Output: "2"
Explanation: Trailing decimal point with no fractional digits; value is exactly 2.
Input: ("2.5",)
Expected Output: "3"
Explanation: Tie: round half away from zero gives 3.
Input: ("-2.5",)
Expected Output: "-3"
Explanation: Tie on a negative magnitude rounds away from zero to -3.
Input: ("0.4",)
Expected Output: "0"
Explanation: First fractional digit 4 < 5, so truncate to 0.
Input: ("-0.6",)
Expected Output: "-1"
Explanation: First fractional digit 6 >= 5, magnitude rounds up to 1, sign preserved.
Input: ("99.9",)
Expected Output: "100"
Explanation: Carry propagates across all 9s, producing a new leading digit.
Input: ("+123.456",)
Expected Output: "123"
Explanation: Explicit '+' sign is dropped; first fractional digit 4 < 5 truncates.
Input: ("123456789012345678901234567890.7",)
Expected Output: "123456789012345678901234567891"
Explanation: 30-digit integer part that overflows float(); string carry adds exactly 1.
Input: ("5",)
Expected Output: "5"
Explanation: No decimal point at all; the value is returned unchanged.
Input: ("-0.4",)
Expected Output: "0"
Explanation: -0.4 truncates to magnitude 0, and -0 is normalized to 0.
Input: (".5",)
Expected Output: "1"
Explanation: Empty integer part treated as 0; tie 0.5 rounds away from zero to 1.
Hints
- Strip the sign first, then split on the decimal point into an integer part and a fractional part; either side may be empty ('-.2' has empty integer part, '2.' has empty fractional part).
- Only the FIRST fractional digit matters for rounding to the nearest integer: if it is 5-9, increment the integer-part string by one with manual carry propagation.
- Remember to re-normalize at the end: drop leading zeros and turn a magnitude of '0' back into a signless '0' so you never return '-0'.
String-Based Round to Nearest Multiple of a Power of Ten
Constraints
- Do not parse s or p with float()/double/Number; operate on the strings directly.
- p is guaranteed to be a positive power of ten: '1', '10', '100', '1000', ... or '0.1', '0.01', ...
- s may carry a sign, an arbitrarily long integer part, and an optional fractional part.
- Rounding is half away from zero; output keeps exactly k decimals when p = 10^-k, else is a plain integer.
Examples
Input: ("12567", "100")
Expected Output: "12600"
Explanation: exp=2; digit at place 1 is '6' (>=5) so the kept prefix '125' becomes '126', then two trailing zeros.
Input: ("1234.678", "0.1")
Expected Output: "1234.7"
Explanation: k=1 decimal; kept fractional digit '6', next dropped digit '7' (>=5) rounds it to '7'.
Input: ("12549", "100")
Expected Output: "12500"
Explanation: exp=2; first dropped digit '4' < 5, so truncate to 12500.
Input: ("12550", "100")
Expected Output: "12600"
Explanation: exp=2; first dropped digit '5' rounds the prefix up to 126, giving 12600.
Input: ("999", "100")
Expected Output: "1000"
Explanation: Carry across all 9s creates a new leading digit before appending the trailing zeros.
Input: ("1234.678", "0.01")
Expected Output: "1234.68"
Explanation: k=2 decimals; dropped digit '8' (>=5) rounds 67 up to 68.
Input: ("-1234.678", "0.1")
Expected Output: "-1234.7"
Explanation: Negative magnitude rounds away from zero; sign preserved on a non-zero result.
Input: ("5", "1000")
Expected Output: "0"
Explanation: Edge case: value far smaller than p; the rounding digit is a virtual leading zero so it rounds down to 0.
Input: ("500", "1000")
Expected Output: "1000"
Explanation: exp=3; the top digit '5' at the rounding place rounds the magnitude up to one multiple of 1000.
Input: ("0.04", "0.1")
Expected Output: "0.0"
Explanation: k=1; first dropped digit '4' < 5 truncates, but one decimal place is preserved as '0.0'.
Input: ("0.05", "0.1")
Expected Output: "0.1"
Explanation: k=1; first dropped digit '5' rounds the tenths place up to 1.
Input: ("7", "1")
Expected Output: "7"
Explanation: exp=0; rounding to the nearest 1 leaves an integer unchanged.
Input: ("-0.04", "0.1")
Expected Output: "0.0"
Explanation: Rounds to magnitude zero; -0.0 is normalized to a positive '0.0'.
Hints
- First convert p into an integer exponent: '100' -> exp 2, '1' -> exp 0, '0.1' -> exp -1, '0.01' -> exp -2. That exponent is the place you round to.
- Split into the exp >= 0 case (round inside/beyond the integer part, output an integer with exp trailing zeros) and the exp < 0 case (keep k = -exp decimal digits). The single 'first dropped digit' at the rounding place decides whether to add one.
- Watch the case where the value is far smaller than p (e.g. round('5','1000')): the first dropped digit is a virtual leading zero, so it must round DOWN to '0', not up to '1000'.