Taschenrechner mit Verlauf

Ein Rechner-Fenster mit Klammern, Potenzen, Wurzel, Prozentrechnung (100 + 19 % = 119), MwSt-Tasten (± 19 % und ± 7 %), Verlauf und Kopieren. Rechnet mit deutschem Komma, Eingabe per Tastatur oder Maus, Enter rechnet, Esc löscht.

taschenrechner.cmd 13,8 KB · läuft unter Windows 10/11 · aktualisiert am 17.10.2026 · 5 Downloads

SHA-256: 968732c627947719b2abf5c3896b904671850cb993a06f5a3afaebaf1c8d12d8

Code

So sieht die Datei aus, die Sie herunterladen (300 Zeilen). Die Kommentare oben im Skript erklären, was es tut.

taschenrechner.cmd
<# : Friederich-IT - Taschenrechner
@echo off
set "FIT_SELF=%~f0"
start "" powershell -NoLogo -NoProfile -STA -ExecutionPolicy Bypass -WindowStyle Hidden -Command "iex ([IO.File]::ReadAllText($env:FIT_SELF,[Text.Encoding]::UTF8))"
exit /b
#>
# ==========================================================================
#  Taschenrechner                                  Version 2.0, Oktober 2026
#  https://friederich-it.de/downloads
#
#  Rechner mit Fenster: ganze Rechnungen eingeben, z. B. (19,99 * 3) + 7 %
#  - Punkt vor Strich, Klammern, Potenz (^), Wurzel (√), Prozent (%), π
#  - Komma oder Punkt als Dezimaltrennzeichen
#  - MwSt-Tasten: +19 % / −19 % (Netto -> Brutto / Brutto -> Netto), 7 %
#  - Verlauf (Doppelklick übernimmt einen Wert), Ergebnis kopieren
#  - Tastatur: Enter = Ergebnis, Esc = Löschen
#  Sicher: Die Eingabe wird von einem eigenen Formel-Parser berechnet und
#  nie als Programmcode ausgeführt. Ändert nichts am System.
# ==========================================================================
$ErrorActionPreference = 'Stop'
$script:de = [Globalization.CultureInfo]::GetCultureInfo('de-DE')

# ---------------------------------------------------------------- Parser
function ConvertTo-Tokens([string]$text) {
    $t = $text -replace '×', '*' -replace '÷', '/' -replace ':', '/' -replace '−', '-' -replace '–', '-' -replace '\bsqrt\b', '√' -replace '\bpi\b', 'π'
    $tokens = New-Object System.Collections.Generic.List[object]
    $i = 0
    while ($i -lt $t.Length) {
        $c = $t[$i]
        if ([char]::IsWhiteSpace($c)) { $i++; continue }
        if ([char]::IsDigit($c) -or (($c -eq ',' -or $c -eq '.') -and $i + 1 -lt $t.Length -and [char]::IsDigit($t[$i + 1]))) {
            $start = $i; $sep = $false
            while ($i -lt $t.Length -and ([char]::IsDigit($t[$i]) -or (($t[$i] -eq ',' -or $t[$i] -eq '.') -and -not $sep))) {
                if ($t[$i] -eq ',' -or $t[$i] -eq '.') { $sep = $true }
                $i++
            }
            if ($i -lt $t.Length -and ($t[$i] -eq 'E' -or $t[$i] -eq 'e')) {
                $j = $i + 1
                if ($j -lt $t.Length -and ($t[$j] -eq '+' -or $t[$j] -eq '-')) { $j++ }
                if ($j -lt $t.Length -and [char]::IsDigit($t[$j])) { while ($j -lt $t.Length -and [char]::IsDigit($t[$j])) { $j++ }; $i = $j }
            }
            $num = $t.Substring($start, $i - $start).Replace(',', '.')
            $tokens.Add(@{ k = 'n'; v = [double]::Parse($num, [Globalization.NumberStyles]::Float, [Globalization.CultureInfo]::InvariantCulture) })
            continue
        }
        if ('+-*/^%()√π'.IndexOf($c) -ge 0) { $tokens.Add(@{ k = [string]$c }); $i++; continue }
        throw "Unbekanntes Zeichen: $c"
    }
    return ,$tokens
}

function Invoke-Calc([string]$text) {
    $script:tk = ConvertTo-Tokens $text
    $script:pos = 0
    if ($script:tk.Count -eq 0) { throw 'Leere Eingabe' }
    $r = Read-Expr
    if ($script:pos -lt $script:tk.Count) { $k = $script:tk[$script:pos].k; if ($k -eq 'n') { throw 'Rechenzeichen zwischen zwei Zahlen fehlt' }; throw "Unerwartetes Zeichen: $k" }
    $v = $r.v
    if ([double]::IsNaN($v) -or [double]::IsInfinity($v)) { throw 'Ergebnis nicht darstellbar' }
    return $v
}
function Peek { if ($script:pos -lt $script:tk.Count) { return $script:tk[$script:pos].k } return '' }
function Read-Expr {
    $left = Read-Term
    while ((Peek) -in '+', '-') {
        $op = (Peek); $script:pos++
        $right = Read-Term
        if ($right.p) { $delta = $left.v * $right.v }      # 200 + 19 % = 238
        else { $delta = $right.v }
        if ($op -eq '+') { $left = @{ v = $left.v + $delta; p = $false } } else { $left = @{ v = $left.v - $delta; p = $false } }
    }
    return $left
}
function Read-Term {
    $left = Read-Unary
    while ((Peek) -in '*', '/', '(', 'π', '√') {
        $op = (Peek)
        if ($op -in '*', '/') { $script:pos++ } else { $op = '*' }   # 2(3+4) oder 2π
        $right = Read-Unary
        if ($op -eq '*') { $left = @{ v = $left.v * $right.v; p = $false } }
        else { if ($right.v -eq 0) { throw 'Division durch 0' }; $left = @{ v = $left.v / $right.v; p = $false } }
    }
    return $left
}
function Read-Power {
    $base = Read-Postfix
    if ((Peek) -eq '^') {
        $script:pos++
        $exp = Read-Unary
        $v = [math]::Pow($base.v, $exp.v)
        if ([double]::IsNaN($v)) { throw 'Potenz nicht definiert' }
        return @{ v = $v; p = $false }
    }
    return $base
}
function Read-Unary {
    if ((Peek) -eq '-') { $script:pos++; $u = Read-Unary; return @{ v = - $u.v; p = $u.p } }
    if ((Peek) -eq '+') { $script:pos++; return Read-Unary }
    if ((Peek) -eq '√') {
        $script:pos++; $u = Read-Unary
        if ($u.v -lt 0) { throw 'Wurzel aus negativer Zahl' }
        return @{ v = [math]::Sqrt($u.v); p = $false }
    }
    return Read-Power
}
function Read-Postfix {
    $r = Read-Primary
    while ((Peek) -eq '%') { $script:pos++; $r = @{ v = $r.v / 100; p = $true } }
    return $r
}
function Read-Primary {
    $k = Peek
    if ($k -eq '') { throw 'Rechnung unvollständig' }
    if ($k -eq 'n') { $v = $script:tk[$script:pos].v; $script:pos++; return @{ v = $v; p = $false } }
    if ($k -eq 'π') { $script:pos++; return @{ v = [math]::PI; p = $false } }
    if ($k -eq '(') {
        $script:pos++
        $r = Read-Expr
        if ((Peek) -ne ')') { throw 'Schließende Klammer fehlt' }
        $script:pos++
        return @{ v = $r.v; p = $false }
    }
    throw "Unerwartetes Zeichen: $k"
}

function Format-Zahl([double]$v) {
    if ($v -eq 0) { return '0' }
    $a = [math]::Abs($v)
    if ($a -ge 1e15 -or $a -lt 1e-9) { return $v.ToString('0.###########E+0', $script:de) }
    $r = [double]::Parse($v.ToString('G12', [Globalization.CultureInfo]::InvariantCulture), [Globalization.CultureInfo]::InvariantCulture)
    return $r.ToString('#,##0.###########', $script:de)
}
function Format-Eingabe([double]$v) {   # Ergebnis als weiterverwendbare Eingabe (ohne Tausenderpunkte)
    $s = Format-Zahl $v
    if ($s -match 'E') { return $s.Replace('.', '').Replace(',', '.') }
    return $s.Replace('.', '')
}

if ($env:FIT_TEST) { return }   # nur für automatische Tests: Fenster nicht öffnen

# ---------------------------------------------------------------- Fenster
Add-Type -AssemblyName System.Windows.Forms, System.Drawing
[System.Windows.Forms.Application]::EnableVisualStyles()

$form = New-Object System.Windows.Forms.Form
$form.Text = 'Taschenrechner – Friederich-IT'
$form.StartPosition = 'CenterScreen'
$form.ClientSize = New-Object System.Drawing.Size(380, 640)
$form.MinimumSize = New-Object System.Drawing.Size(360, 560)
$form.Font = New-Object System.Drawing.Font('Segoe UI', 10)
$form.KeyPreview = $true
$form.BackColor = [System.Drawing.Color]::FromArgb(238, 241, 244)

$eingabe = New-Object System.Windows.Forms.TextBox
$eingabe.Font = New-Object System.Drawing.Font('Segoe UI', 16)
$eingabe.TextAlign = 'Right'
$eingabe.Dock = 'Top'
$eingabe.BorderStyle = 'FixedSingle'

$ergebnis = New-Object System.Windows.Forms.Label
$ergebnis.Font = New-Object System.Drawing.Font('Segoe UI Semibold', 22)
$ergebnis.TextAlign = 'MiddleRight'
$ergebnis.Dock = 'Top'
$ergebnis.Height = 52
$ergebnis.ForeColor = [System.Drawing.Color]::FromArgb(20, 33, 46)
$ergebnis.Text = '0'

$hinweis = New-Object System.Windows.Forms.Label
$hinweis.Dock = 'Top'
$hinweis.Height = 22
$hinweis.TextAlign = 'MiddleRight'
$hinweis.ForeColor = [System.Drawing.Color]::FromArgb(86, 102, 119)

$tasten = New-Object System.Windows.Forms.TableLayoutPanel
$tasten.Dock = 'Top'
$tasten.Height = 330
$tasten.ColumnCount = 5
$tasten.RowCount = 6
for ($c = 0; $c -lt 5; $c++) { [void]$tasten.ColumnStyles.Add((New-Object System.Windows.Forms.ColumnStyle('Percent', 20))) }
for ($r = 0; $r -lt 6; $r++) { [void]$tasten.RowStyles.Add((New-Object System.Windows.Forms.RowStyle('Percent', 16.66))) }

$verlauf = New-Object System.Windows.Forms.ListBox
$verlauf.Dock = 'Fill'
$verlauf.Font = New-Object System.Drawing.Font('Consolas', 10)
$verlauf.IntegralHeight = $false

$verlaufTitel = New-Object System.Windows.Forms.Label
$verlaufTitel.Text = 'Verlauf (Doppelklick übernimmt das Ergebnis)'
$verlaufTitel.Dock = 'Top'
$verlaufTitel.Height = 24
$verlaufTitel.ForeColor = [System.Drawing.Color]::FromArgb(86, 102, 119)

$tip = New-Object System.Windows.Forms.ToolTip
$script:letzterWert = $null

function Update-Vorschau {
    $txt = $eingabe.Text
    if ($txt.Trim() -eq '') { $ergebnis.Text = '0'; $hinweis.Text = ''; $script:letzterWert = $null; return }
    try {
        $v = Invoke-Calc $txt
        $script:letzterWert = $v
        $ergebnis.Text = Format-Zahl $v
        $ergebnis.ForeColor = [System.Drawing.Color]::FromArgb(20, 33, 46)
        $hinweis.Text = ''
    } catch {
        $script:letzterWert = $null
        $ergebnis.ForeColor = [System.Drawing.Color]::FromArgb(150, 160, 170)
        $hinweis.Text = $_.Exception.Message
    }
}
function Add-Verlauf([string]$zeile) { $verlauf.Items.Insert(0, $zeile); if ($verlauf.Items.Count -gt 100) { $verlauf.Items.RemoveAt(100) } }
function Set-Ergebnis {
    try {
        $v = Invoke-Calc $eingabe.Text
        Add-Verlauf ("{0} = {1}" -f $eingabe.Text.Trim(), (Format-Zahl $v))
        $eingabe.Text = Format-Eingabe $v
        $eingabe.SelectionStart = $eingabe.Text.Length
    } catch { $hinweis.Text = $_.Exception.Message; [System.Media.SystemSounds]::Beep.Play() }
}
function Insert-Text([string]$s) {
    $start = $eingabe.SelectionStart
    $eingabe.Text = $eingabe.Text.Remove($start, $eingabe.SelectionLength).Insert($start, $s)
    $eingabe.SelectionStart = $start + $s.Length
    $eingabe.Focus()
}
function Invoke-Mwst([double]$faktor, [string]$beschreibung, [bool]$heraus) {
    try { $v = Invoke-Calc $eingabe.Text } catch { $hinweis.Text = 'Zuerst einen Betrag eingeben'; return }
    $neu = if ($heraus) { $v / $faktor } else { $v * $faktor }
    Add-Verlauf ("{0} {1} = {2}" -f (Format-Zahl $v), $beschreibung, (Format-Zahl $neu))
    $eingabe.Text = Format-Eingabe ([math]::Round($neu, 2))
    $eingabe.SelectionStart = $eingabe.Text.Length
    $eingabe.Focus()
}

$belegung = @(
    @('C', 'Alles löschen (Esc)'), @('⌫', 'Letztes Zeichen löschen'), @('(', ''), @(')', ''), @('÷', 'Teilen'),
    @('7', ''), @('8', ''), @('9', ''), @('√', 'Wurzel, z. B. √16'), @('×', 'Malnehmen'),
    @('4', ''), @('5', ''), @('6', ''), @('^', 'Potenz, z. B. 2^10'), @('−', 'Minus'),
    @('1', ''), @('2', ''), @('3', ''), @('%', 'Prozent: 200 + 19 % = 238, 50 % * 80 = 40'), @('+', 'Plus'),
    @('π', 'Kreiszahl Pi'), @('0', ''), @(',', 'Komma'), @('x²', 'Quadrat'), @('=', 'Ergebnis (Enter)'),
    @('+19 %', 'Netto -> Brutto mit 19 % MwSt'), @('−19 %', 'Brutto -> Netto: 19 % MwSt herausrechnen'), @('+7 %', 'Netto -> Brutto mit 7 % MwSt'), @('−7 %', 'Brutto -> Netto: 7 % MwSt herausrechnen'), @('Kopieren', 'Ergebnis in die Zwischenablage')
)
foreach ($b in $belegung) {
    $btn = New-Object System.Windows.Forms.Button
    $btn.Text = $b[0]
    $btn.Dock = 'Fill'
    $btn.Margin = New-Object System.Windows.Forms.Padding(3)
    $btn.FlatStyle = 'Flat'
    $btn.FlatAppearance.BorderColor = [System.Drawing.Color]::FromArgb(211, 218, 225)
    $btn.BackColor = [System.Drawing.Color]::White
    $btn.Font = New-Object System.Drawing.Font('Segoe UI', $(if ($b[0].Length -gt 2) { 9 } else { 13 }))
    if ($b[0] -match '^[0-9,π]$') { $btn.BackColor = [System.Drawing.Color]::FromArgb(250, 251, 252) }
    if ($b[0] -eq '=') { $btn.BackColor = [System.Drawing.Color]::FromArgb(0, 109, 119); $btn.ForeColor = [System.Drawing.Color]::White }
    if ($b[0] -match 'MwSt|%$' -and $b[0].Length -gt 1) { $btn.BackColor = [System.Drawing.Color]::FromArgb(223, 241, 242) }
    if ($b[1]) { $tip.SetToolTip($btn, $b[1]) }
    $btn.TabStop = $false
    $btn.Add_Click({
        param($sender, $e)
        switch ($sender.Text) {
            'C'        { $eingabe.Text = ''; $eingabe.Focus() }
            '⌫'        { if ($eingabe.SelectionLength -gt 0) { Insert-Text '' } elseif ($eingabe.SelectionStart -gt 0) { $p = $eingabe.SelectionStart; $eingabe.Text = $eingabe.Text.Remove($p - 1, 1); $eingabe.SelectionStart = $p - 1; $eingabe.Focus() } }
            '='        { Set-Ergebnis }
            'x²'       { Insert-Text '^2' }
            '+19 %'    { Invoke-Mwst 1.19 '+ 19 % MwSt' $false }
            '−19 %'    { Invoke-Mwst 1.19 '- 19 % MwSt herausgerechnet' $true }
            '+7 %'     { Invoke-Mwst 1.07 '+ 7 % MwSt' $false }
            '−7 %'     { Invoke-Mwst 1.07 '- 7 % MwSt herausgerechnet' $true }
            'Kopieren' { if ($null -ne $script:letzterWert) { [System.Windows.Forms.Clipboard]::SetText((Format-Zahl $script:letzterWert)); $hinweis.Text = 'Ergebnis kopiert' } }
            '×'        { Insert-Text ' × ' }
            '÷'        { Insert-Text ' ÷ ' }
            '−'        { Insert-Text ' − ' }
            '+'        { Insert-Text ' + ' }
            default    { Insert-Text $sender.Text }
        }
    })
    $tasten.Controls.Add($btn)
}

$eingabe.Add_TextChanged({ Update-Vorschau })
$verlauf.Add_DoubleClick({
    if ($verlauf.SelectedItem) {
        $wert = ([string]$verlauf.SelectedItem -split ' = ')[-1]
        Insert-Text ($wert.Replace('.', ''))
    }
})
$form.Add_KeyDown({
    param($sender, $e)
    if ($e.KeyCode -eq 'Enter') { Set-Ergebnis; $e.SuppressKeyPress = $true }
    elseif ($e.KeyCode -eq 'Escape') { $eingabe.Text = ''; $e.SuppressKeyPress = $true }
})

$form.Controls.Add($verlauf)
$form.Controls.Add($verlaufTitel)
$form.Controls.Add($tasten)
$form.Controls.Add($hinweis)
$form.Controls.Add($ergebnis)
$form.Controls.Add($eingabe)
$form.Add_Shown({ $eingabe.Focus() })
[void]$form.ShowDialog()