*! version 2.7.0  2026-04-08
*! Author: Anne Fengyan Shi
program define round_exact, rclass
    version 14.0
    syntax anything(name=input) [if] [in], D(integer) [Generate(name) Replace]
    // ---------------------------------------------------------
    // Syntax Trap for Parentheses
    // ---------------------------------------------------------
    if substr(trim("`input'"), 1, 1) == "(" {
        display as error "Syntax Error: Do not use parentheses around the variable name."
        display as error "Correct usage: {bf:round_exact1 varname, d(2) replace}"
        exit 198
    }
    local multiplier = 10^`d'

    // ---------------------------------------------------------
    // Epsilon rationale:
    //   3.05 is stored as 3.04999999999999982236...
    //   3.05 * 10 = 30.4999999999999982236...
    //   shortfall from 30.5 = ~1.8e-15 * 10 = ~1.8e-14
    //   but for float (single precision) values like gear_ratio,
    //   the shortfall can be as large as ~4.4e-7 * multiplier
    //   We use eps = 1e-6 / multiplier so after multiplying:
    //     abs(x)*multiplier + 0.5 + 1e-6
    //   which comfortably bridges the gap without affecting
    //   values genuinely below the rounding boundary
    //   (those would need to be within 1e-6 of .5, which does
    //   not occur for typical numeric data with d<=4)
    // ---------------------------------------------------------
    local eps = 1e-6

    // ---------------------------------------------------------
    // CASE 1: Variable Mode
    // ---------------------------------------------------------
    capture confirm variable `input'
    if !_rc {
        marksample touse

        if "`generate'" == "" & "`replace'" == "" {
            display as error "You must specify either {bf:generate(newvar)} or {bf:replace}."
            exit 198
        }

        if "`generate'" != "" {
            confirm new variable `generate'
            quietly gen double `generate' = `input' if `touse'
            quietly replace `generate' = ///
                sign(`input') * floor(abs(`input') * `multiplier' + 0.5 + `eps') / `multiplier' ///
                if `touse'
            display as text "Variable " as result "`generate'" as text " created."
        }
        else if "`replace'" != "" {
            quietly count if `touse'
            local n_changed = r(N)
            quietly replace `input' = ///
                sign(`input') * floor(abs(`input') * `multiplier' + 0.5 + `eps') / `multiplier' ///
                if `touse'
            display as text "Variable " as result "`input'" as text " updated." ///
                " (" as result "`n_changed'" as text " real change(s) made)"
            return scalar N_changed = `n_changed'
        }
    }

    // ---------------------------------------------------------
    // CASE 2: Scalar/Literal Mode
    // ---------------------------------------------------------
    else {
        tempname res
        scalar `res' = sign(`input') * ///
            floor(abs(`input') * `multiplier' + 0.5 + `eps') / `multiplier'
        display as result `res'
        return scalar val = `res'
    }
end