longevity: la copertura si confronta grezza, non arrotondata
Bug in aggrega(): la soglia veniva confrontata con la copertura gia' arrotondata a due decimali, quindi 0.396 (< 0.40) passava per 'ok' perche' arrotondava a 0.40. Confronto ora sulla copertura grezza; l'arrotondamento resta solo sul valore esposto. Generalizzata arrotonda1 in curve.ts a un numero qualsiasi di decimali (bit-exact, banker's rounding) e riusata qui a due decimali, invece di tenere una seconda logica di arrotondamento nel motore.
This commit is contained in:
@@ -31,27 +31,31 @@ export type Punteggio =
|
||||
/** Una voce da aggregare: punteggio 0-100 (o null se il dato manca) e il suo peso nominale. */
|
||||
export type VocePesata = { punteggio: number | null; peso: number };
|
||||
|
||||
/** Arrotonda a due cifre decimali, come `round(x, 2)` per la copertura nell'oracolo. */
|
||||
function arrotonda2(x: number): number {
|
||||
return Math.round(x * 100) / 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* Media pesata delle voci disponibili, con i pesi delle voci mancanti
|
||||
* ridistribuiti su quelle presenti. Come `aggregate` nell'oracolo, con la
|
||||
* differenza di tipo descritta sopra.
|
||||
*
|
||||
* ATTENZIONE (bug corretto, trovato in revisione): il confronto con la soglia
|
||||
* usa la copertura GREZZA (`pesoDisponibile / pesoTotale`), non quella
|
||||
* arrotondata. L'arrotondamento a due decimali (`arrotonda1(.., 2)`) serve
|
||||
* solo al valore esposto nel campo `copertura` del risultato. Confrontare la
|
||||
* copertura gia' arrotondata avrebbe fatto passare per 'ok' una copertura
|
||||
* grezza appena sotto 0.40 che arrotonda a 0.40 (es. 0.396) - esattamente il
|
||||
* numero pieno fasullo che il tipo `Punteggio` esiste per impedire.
|
||||
*/
|
||||
export function aggrega(voci: VocePesata[]): Punteggio {
|
||||
const pesoTotale = voci.reduce((s, v) => s + v.peso, 0);
|
||||
const disponibili = voci.filter((v) => v.punteggio !== null);
|
||||
const pesoDisponibile = disponibili.reduce((s, v) => s + v.peso, 0);
|
||||
const copertura = pesoTotale ? arrotonda2(pesoDisponibile / pesoTotale) : 0;
|
||||
const coperturaGrezza = pesoTotale ? pesoDisponibile / pesoTotale : 0;
|
||||
const copertura = arrotonda1(coperturaGrezza, 2);
|
||||
|
||||
if (disponibili.length === 0) {
|
||||
return { stato: 'insufficiente', copertura: 0 };
|
||||
}
|
||||
|
||||
if (copertura < COPERTURA_MINIMA) {
|
||||
if (coperturaGrezza < COPERTURA_MINIMA) {
|
||||
return { stato: 'insufficiente', copertura };
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user