Rundungsfehler beim Übertritt

Eine Gesamtdurchschnittsnote von \(2{,}33\) bescheinigt in Bayern die Eignung für das Gymnasium, \(2{,}34\) nicht mehr. Bei dieser scharfen Grenze kann die Reihenfolge zweier Rundungen entscheiden, auf welcher Seite ein Kind landet. In einem Blogbeitrag von Günther Felbinger werden die möglichen Folgen dieses Rundungseffekts diskutiert. Rein Interesse halber habe ich die Auswirkungen selbst über alle im Modell zugelassenen Notenkombinationen hinweg berechnet.


Im Mittelpunkt steht für mich die Frage: Wie oft führt die frühe Rundung auf ganze Fachnoten zu einer anderen rechnerischen Schulart als die Rechnung mit ungerundeten Fachmittelwerten? Außerdem wäre interessant, was sich ändert, wenn die Mittelwerte der einzelnen Fächer (wie dort vorgeschlagen) zunächst nur auf eine Nachkommastelle gerundet werden.

Nach § 6 der Bayerischen Grundschulordnung wird die Durchschnittsnote aus den Noten in Deutsch, Mathematik sowie Heimat- und Sachunterricht gebildet. Die Ausführungsbekanntmachung formuliert die Grenzen als bis einschließlich \(2{,}33\) für das Gymnasium und bis einschließlich \(2{,}66\) für die Realschule. Im folgenden Rechenmodell wird jeder Gesamtdurchschnitt auf zwei Dezimalstellen abgeschnitten und nicht erneut gerundet. Damit gilt beispielsweise

\[
\frac{2{,}33+2{,}34+2{,}34}{3}=2{,}3366\ldots \longrightarrow 2{,}33.
\]

Für die Einstufung ist das entscheidend: \(2{,}3366\ldots\) wird nicht zu \(2{,}34\), sondern zu \(2{,}33\) und reicht damit noch für das Gymnasium.

Der Ausgangsbeitrag veranschaulicht die frühe Rundung mit den angenommenen Fachmittelwerten \(1{,}6\), \(2{,}6\) und \(2{,}6\). Daraus werden zunächst die ganzen Zeugnisnoten \(2\), \(3\) und \(3\). Deren Mittelwert ist

\[
\frac{2+3+3}{3}=2{,}6666\ldots \longrightarrow 2{,}66,
\]

womit die direkte Gymnasialeignung verfehlt wird. Ohne die Rundung auf ganze Fachnoten ergäbe sich in dem fiktiven Beispiel hingegen

\[
\frac{1{,}6+2{,}6+2{,}6}{3}=2{,}2666\ldots \longrightarrow 2{,}26.
\]

Im zweiten Beispiel wirkt die Rundung in die andere Richtung: Aus \(2{,}4\), \(3{,}4\) und \(2{,}4\) entstehen die ganzen Notenwerte \(2\), \(3\) und \(2\), also offiziell \(2{,}33\). Die ungerundeten Werte ergeben hingegen \(2{,}73\). Die Beispiele zeigen den Effekt deutlich. Wie häufig er auftritt und welche Auswirkungen eine andere Rundung hätte, lässt sich daraus aber noch nicht ableiten.

Für meine Berechnung verwende ich deshalb einen klaren Ergebnisraum. Jeder Fachmittelwert kann darin einen der \(501\) Werte

\[
1{,}00,\, 1{,}01,\, 1{,}02,\, \ldots, \, 5{,}99, \, 6{,}00
\]

annehmen. Ich gewichte alle geordneten Kombinationen aus Deutsch, Mathematik und Heimat- und Sachunterricht gleich. Es gibt somit

\[
501^3=125\,751\,501
\]

Fälle. Das Modell ist damit eindeutig, aber künstlich: Es setzt gleichverteilte Fachmittelwerte, die Unabhängigkeit der drei Fächer und Schritte von genau einem Hundertstel voraus. Die tatsächliche Verteilung bayerischer Grundschulkinder bildet es nicht ab.

Für drei Fachmittelwerte \(d,m,h\) schreiben wir

\[
A(d,m,h)=\frac{\left\lfloor 100\cdot\frac{d+m+h}{3}\right\rfloor}{100}
\]

für den auf zwei Dezimalstellen abgeschnittenen Gesamtdurchschnitt. Nun vergleichen wir drei Rechenwege:

\[
\begin{aligned}
A_0 &= A(\operatorname{round}(d),\operatorname{round}(m),\operatorname{round}(h)),\\
A_1 &= A(\operatorname{round}_1(d),\operatorname{round}_1(m),\operatorname{round}_1(h)),\\
A_\mathrm{ref} &= A(d,m,h).
\end{aligned}
\]

\(A_0\) entspricht der Rechnung mit ganzen Zeugnisnoten, \(A_1\) dem Vorschlag mit einer Nachkommastelle und \(A_\mathrm{ref}\) der ungerundeten Referenz innerhalb des Modells. Die Berechnung lässt sich in beispielsweise in Rust direkt umsetzen:

const SCHOOL_GYMNASIUM: i32 = 0;
const SCHOOL_REALSCHULE: i32 = 1;
const SCHOOL_MITTELSCHULE: i32 = 2;
const FLOAT_TOLERANCE: f64 = 1e-9;

#[derive(Clone, Copy, Default)]
struct Statistic {
    conflict_count: u64,
    total_count: u64,
}

fn determine_school(average: f64) -> i32 {
    if average <= 2.33 {
        return SCHOOL_GYMNASIUM;
    }
    if average <= 2.66 {
        return SCHOOL_REALSCHULE;
    }
    SCHOOL_MITTELSCHULE
}

fn calculate_average(note_deutsch: f64, note_mathe: f64, note_hsu: f64) -> f64 {
    let average = (note_deutsch + note_mathe + note_hsu) / 3.0;
    ((average + FLOAT_TOLERANCE) * 100.0).floor() / 100.0
}

fn round_to_whole_grade(grade: f64) -> f64 {
    grade.round()
}

fn round_to_one_decimal(grade: f64) -> f64 {
    (grade * 10.0).round() / 10.0
}

fn normalize_grade(grade: f64) -> f64 {
    (grade * 100.0).round() / 100.0
}

fn percentage(count: u64, total: u64) -> f64 {
    count as f64 / total as f64 * 100.0
}

fn main() {
    let mut combination_count = 0;
    let mut official_reference_conflicts = 0;
    let mut official_proposal_conflicts = 0;
    let mut proposal_reference_conflicts = 0;
    let mut statistics = [Statistic::default(); 4];

    let mut note_deutsch = 1.0;
    while note_deutsch <= 6.0 {
        let mut note_mathe = 1.0;
        while note_mathe <= 6.0 {
            let mut note_hsu = 1.0;
            while note_hsu <= 6.0 {
                combination_count += 1;

                let official_average = calculate_average(
                    round_to_whole_grade(note_deutsch),
                    round_to_whole_grade(note_mathe),
                    round_to_whole_grade(note_hsu),
                );
                let proposed_average = calculate_average(
                    round_to_one_decimal(note_deutsch),
                    round_to_one_decimal(note_mathe),
                    round_to_one_decimal(note_hsu),
                );
                let reference_average =
                    calculate_average(note_deutsch, note_mathe, note_hsu);
                let official_school = determine_school(official_average);
                let proposed_school = determine_school(proposed_average);
                let reference_school = determine_school(reference_average);

                if official_average == 3.0 {
                    statistics[0].total_count += 1;
                    if reference_school != SCHOOL_MITTELSCHULE {
                        statistics[0].conflict_count += 1;
                    }
                }
                if official_average == 2.66 {
                    statistics[1].total_count += 1;
                    if reference_school == SCHOOL_GYMNASIUM {
                        statistics[1].conflict_count += 1;
                    }
                }
                if official_average == 2.33 {
                    statistics[2].total_count += 1;
                    if reference_school != SCHOOL_GYMNASIUM {
                        statistics[2].conflict_count += 1;
                    }
                }
                if official_average == 2.66 {
                    statistics[3].total_count += 1;
                    if reference_school == SCHOOL_MITTELSCHULE {
                        statistics[3].conflict_count += 1;
                    }
                }

                if official_school != reference_school {
                    official_reference_conflicts += 1;
                }
                if official_school != proposed_school {
                    official_proposal_conflicts += 1;
                }
                if proposed_school != reference_school {
                    proposal_reference_conflicts += 1;
                }

                note_hsu = normalize_grade(note_hsu + 0.01);
            }
            note_mathe = normalize_grade(note_mathe + 0.01);
        }
        note_deutsch = normalize_grade(note_deutsch + 0.01);
    }

    println!(
        "official/reference: {}%",
        percentage(official_reference_conflicts, combination_count)
    );
    println!(
        "official/proposal: {}%",
        percentage(official_proposal_conflicts, combination_count)
    );
    println!(
        "proposal/reference: {}%",
        percentage(proposal_reference_conflicts, combination_count)
    );
    for (index, statistic) in statistics.iter().enumerate() {
        println!(
            "#{}: {}% ({}/{})",
            index + 1,
            percentage(statistic.conflict_count, statistic.total_count),
            statistic.conflict_count,
            statistic.total_count
        );
    }
}

Die kleine Toleranz in calculate_average verhindert, dass ein Wert wie \(2{,}67\) wegen seiner binären Float-Darstellung versehentlich als \(2{,}669999\ldots\) abgeschnitten wird. normalize_grade führt zudem jeden Schleifenwert wieder auf das Raster eines Hundertstels zurück. Zwei verschiedene mögliche Mittelwerte liegen mindestens \(1/300\) auseinander; die Toleranz von \(10^{-9}\) ist also klein genug, um keinen echten Grenzfall zu verändern.

Kompiliert und gestartet wird das Programm mit

rustc -O calc.rs && ./calc

Das Programm durchläuft alle \(125\,751\,501\) Kombinationen und liefert:

VergleichAnteil
Ganze Fachnoten gegenüber ungerundeten Fachmittelwerten\(10{,}4411\,\%\)
Ganze Fachnoten gegenüber dem Vorschlag mit einer Nachkommastelle\(10{,}1521\,\%\)
Vorschlag mit einer Nachkommastelle gegenüber ungerundeten Fachmittelwerten\(0{,}6615\,\%\)

Der für mich zentrale Wert ist \(10{,}4411\,\%\): In mehr als jeder zehnten Kombination führt die frühe Rundung auf ganze Fachnoten zu einer anderen rechnerischen Schulart als die ungerundete Referenz. Dieser Anteil beschreibt ausschließlich den Rundungseffekt innerhalb des gewählten Modells.

Eine Umstellung auf Fachmittelwerte mit einer Nachkommastelle würde in \(10{,}1521\,\%\) aller Modellfälle zu einer anderen Einstufung als das bisherige Verfahren führen. Entscheidend ist aber der dritte Vergleich: Gegenüber der ungerundeten Referenz bleiben beim Verfahren mit einer Nachkommastelle nur \(0{,}6615\,\%\) abweichende Fälle übrig. Die Abweichung von der Referenz sinkt damit um

\[
1-\frac{0{,}6615}{10{,}4411}\approx 93{,}7\,\%.
\]

Genau darin liegt für mich die fragwürdige Auswirkung des bisherigen Rechenwegs: An den angenommenen Fachleistungen ändert sich nichts, allein der Zeitpunkt der Rundung kann die rechnerische Schulart verändern. Die Rechnung mit einer Nachkommastelle beseitigt diesen Effekt zwar nicht vollständig, reduziert ihn im Modell aber sehr deutlich.

Diese Zahlen sind dennoch keine Aussage darüber, wie viele Kinder tatsächlich betroffen sind. Zeugnisnoten entstehen nicht zwingend als mechanisch gerundete Mittelwerte, die Fachleistungen sind weder gleichverteilt noch unabhängig, und die tatsächliche Wahl der Schulart hängt nicht nur von diesen drei Zahlen ab. Meine Berechnung beantwortet eine engere Frage: Wie oft ändert die Rundung die rechnerische Einstufung, wenn alle Hundertstelkombinationen gleich wahrscheinlich sind?

Zum Schluss der Vergleich mit dem Ausgangsbeitrag: Die dort genannte Gesamtquote von etwa \(10\,\%\) liegt nahe an meinen \(10{,}4411\,\%\). Die vier mit jeweils \(1/6 \approx 17\,\%\) angegebenen Einzelfälle kann meine Berechnung dagegen nicht bestätigen. Für „mit \(3{,}00\) die Realschule verfehlt“, „mit \(2{,}66\) das Gymnasium verfehlt“, „mit \(2{,}33\) das Gymnasium erreicht“ und „mit \(2{,}66\) die Realschule erreicht“ erhalte ich der Reihe nach \(1{,}4615\,\%\), \(1{,}2023\,\%\), \(62{,}9395\,\%\) und \(59{,}6096\,\%\).

Die vier Angaben sind bedingte Wahrscheinlichkeiten mit unterschiedlich großen Bezugsgruppen. Ohne einen abweichend definierten Ergebnisraum lässt sich \(1/6\) daraus nicht herleiten. Meine Berechnung bestätigt somit den Rundungseffekt, nicht aber diese Einzelwahrscheinlichkeiten. Für Aussagen über tatsächlich betroffene Kinder wären ohnehin reale Notenverteilungen und deren Abhängigkeiten nötig.

Zurück