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.
SHA-256:
968732c627947719b2abf5c3896b904671850cb993a06f5a3afaebaf1c8d12d8 Code
So sieht die Datei aus, die Sie herunterladen (300 Zeilen). Die Kommentare oben im Skript erklären, was es tut.
<# : 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()