Come sommare i numeri float nel formato ore e minuti?


14

Come sommare i numeri float nel formato ore e minuti?

Coloro. se calcola due di questi numeri 0.35+3.45allora dovrebbe essere = 4.20, no3.80

var arr = [0.15, 0.2, 3.45, 0.4, 2, 0.3, 5.2, 1, 1.4, 1.1, 2.4, 1, 3.4]
    
var sum = 0.0;
    
arr.forEach(function(item) {
  console.log(sum);
  sum += parseFloat(item);
});

Il risultato dovrebbe essere così:

0
0.15
0.35
4.20
5.0
7.0
7.30
12.50
13.50
15.30
16.40
19.20
20.20

1
Come definirai la logica?
Juhil Somaiya,

6
Quindi 0,2 rappresenta 20 minuti, o 0,2 di un'ora? 1.0 significa un'ora o 0.6 significa un'ora?
Spangle del

2
Siamo spiacenti, quindi 1.0 significa 1 ora e 0 minuti ?. Cosa significano, ad esempio, 0.6 e 0.7?
Spangle del

2
Troverai una risposta qui: codereview.stackexchange.com/a/109478 . Converti i tuoi numeri in stringhe e nella funzione usa "."per dividere quelle stringhe, no ":".
Gerardo Furtado,

6
Ti complichi con tale logica. 0.5le ore dovrebbero essere di 30 minuti, qualsiasi altra cosa è completamente irrazionale. Spero non sia per un cliente nella vita reale. O lavori con valori float di un'unità di tempo fissa o separa ore / minuti. Così semplice.
Kaddath,

Risposte:


7

Il modo migliore per gestire formati di dati "folli" come quello è prima convertirli in un formato sano e facilmente elaborabile, fare tutto ciò che è necessario fare usando i dati convertiti e infine (se assolutamente necessario) convertire i risultati in il formato originale.

(Naturalmente, la vera soluzione è smettere di usare del tutto tali rappresentazioni del tempo folle. Ma non è sempre pratico, ad esempio perché è necessario interfacciarsi con un sistema legacy che non è possibile modificare.)

Nel tuo caso, un formato sano adatto per i tuoi dati sarebbe ad esempio un numero integrale di minuti. Dopo aver convertito i tuoi dati in questo formato, puoi quindi fare l'aritmetica ordinaria su di essi.

// converts a pseudo-float of the form hh.mm into an integer number of minutes
// robust against floating-point roundoff errors, also works for negative numbers
function hhMmToMinutes(x) {
  const hhmm = Math.round(100 * x)  // convert hh.mm -> hhmm
  return 60 * Math.trunc(hhmm / 100) + (hhmm % 100)
}

// convert minutes back to hh.mm pseudo-float format
// use minutesToHhMm(minutes).toFixed(2) if you want trailing zeros
function minutesToHhMm(minutes) {
  return Math.trunc(minutes / 60) + (minutes % 60) / 100
}

const arr = [0.15, 0.2, 3.45, 0.4, 2, 0.3, 5.2, 1, 1.4, 1.1, 2.4, 1, 3.4]

let sum = 0
console.log( arr.map(hhMmToMinutes).map(x => sum += x).map(minutesToHhMm) )

Si noti che il codice di conversione sopra moltiplica per primo il float di input per 100 e lo arrotonda a un numero intero prima di separare le parti di ora e minuti. Ciò dovrebbe gestire in modo efficace gli input con possibili errori di arrotondamento evitando la necessità di stringere gli input.

Il motivo per cui è necessario prestare particolare attenzione qui è perché il numero 0,01 = 1/100 (e la maggior parte dei suoi multipli) non è in realtà esattamente rappresentabile nel formato binario a virgola mobile utilizzato da JavaScript. Quindi non puoi effettivamente avere un numero JS che sarebbe esattamente uguale a 0,01 - il migliore che puoi avere è un numero così vicino che la conversione in una stringa nasconderà automaticamente l'errore. Ma l'errore di arrotondamento è ancora lì e può morderti se provi a fare cose come confrontare tali numeri con una soglia esatta. Ecco una bella e semplice dimostrazione:

console.log(Math.trunc(100 * 0.29))  // you'd think this would be 29...


7

// We got this after simple addition
// Now we want to change it into 4.2
sample = 3.8

// Now here are the minutes that the float currently shows
minutes = (sample % 1) * 100

// And the hours
hours = Math.floor(sample)

// Here are the number of hours that can be reduced from minutes
AddHours = Math.floor(minutes / 60)

// Adding them to the hours count
hours += AddHours

// Reducing mintues
minutes %= 60

// Finally formatting hour and minutes into your format
final = hours + (minutes / 100.0)
console.log(final)

Puoi usare questa logica dopo aver fatto una semplice aggiunta aritmetica, questo convertirà la somma in formato orario


2
Perché math.floor(sample/1)invece di solo math.floor(sample)?
selbie

2
@selbie, semplicemente perché ho solo aggiunto per la prima volta sample / 1, ho pensato che JS lo pavimentasse da solo perché non c'era decimale nel divisore (come in sample / 1.0, abbastanza tempo da quando ho usato JS;), ma non è stato così, quindi ho rapidamente cercato su Google e aggiunto floor, ho dimenticato di rimuoverlo. Comunque grazie per
averlo

2
Dovresti dire che questo deve essere fatto per ogni elemento e arrnon sul risultato. Ad esempio, se arr = [1.5, 2.5]il risultato sarà 4invece di4.4
Tito

1
@Titus, Sì, ho perso quella parte, quindi dobbiamo recuperare ore e minuti da ogni singolo elemento, quindi aggiungerli separatamente, quindi solo il mio approccio sarà utile.
Letsintegreat,

4

Ecco qui, l'implementazione in Javascript ES6. Ho fatto un ciclo di ogni elemento e ho diviso ore, minuti ., verificato se i minuti sono inesistenti altrimenti assegnare 0 contare il totale di ore e minuti e applicato il calcolo necessario.

const resultObj = [0.35, 3.45].reduce((a, e) => {
  const [hours, minutes] = e.toString().split('.');
  a.h += +hours;
  a.m += (+(minutes.padEnd(2, 0)) || 0); 
  return a;
}, {h: 0, m: 0});


const result = resultObj.h + Math.floor(resultObj.m / 60) + ((resultObj.m % 60) / 100);
console.log(result);


1
Non funzionerà così com'è, ad es. 0,3 verranno conteggiati solo come tre minuti, non 30 minuti con la soluzione.
Spangle del

3

Devi convertire tutto in ore o in minuti e poi fare i conti.

Example: 0.35 + 3.45
Convert 0.35 to Hours Number((35/60).toFixed(2))
= 0.58

Then convert 3hours 45 minutes into hours
= 3 + Number((45/60).toFixed(2))
= 3hours + 0.75 hours
= 3.75

Add the 2 conversions
= 0.58 + 3.75
= 4.33 hours

Convert the 0.33 back to minutes by multiplying by 60
= 4 hours 19.8 minutes ~ 20 mins

Hope that helps!

2

Devi prima convertirli in ore e minuti

var arr = [0.15, 0.2, 3.45, 0.4, 2, 0.3, 5.2, 1, 1.4, 1.1, 2.4, 1, 3.4];

function add(arr) {
  var hm = arr
    .map(v => [Math.floor(v), (v % 1).toFixed(2) * 100])  // Map the array to values having [hours, minutes]
    .reduce((t, v) => { //  reduce the array of [h, m] to a single object having total hours and minutes
      t = {
        h: t.h + v[0],
        m: t.m + v[1]
      };
      return t;
    }, {
      h: 0,
      m: 0
    });

  // final result would be = 
  // total hours from above
  // + hours from total minutes (retrived using Math.floor(hm.m / 60)) 
  // + minutes (after converting it to be a decimal part)
  return (parseFloat(hm.h + Math.floor(hm.m / 60)) + parseFloat(((hm.m % 60) / 100).toFixed(2))).toFixed(2);
}

// adding `arr` array
console.log(add(arr));

// adding 0.35+3.45
console.log(add([0.35, 3.45]));


2
Sfortunatamente nessuno lo notò all'inizio. E sebbene probabilmente sia corretto, da dove viene l'apprendimento? Copia e incolla dall'OP della tua soluzione aiuta molto ovviamente a breve termine. Ma era una domanda intelligente che merita una spiegazione intelligente.
GetSet

2

Posso sapere perché vuoi che il primo risultato sia 0 ? Di seguito è riportato un modo per farlo con un singolo loop. I commenti sono nel codice per spiegare. Con ogni ciclo aggiungiamo i minuti e se sono maggiori di 60, aggiungiamo un'ora e quindi usiamo % 60 per ottenere i minuti rimanenti. I galleggianti possono essere un dolore con cui lavorare,

ad es. 1.1 + 2.2 === 3.3 è falso

Quindi ho convertito i numeri in una stringa e poi di nuovo in un int.

var arr = [0.15, 0.2, 3.45, 0.4, 2, 0.3, 5.2, 1, 1.4, 1.1, 2.4, 1, 3.4];
let hoursTally = 0;
let minutesTally = 0;

for(var i = 0; i < arr.length; i++) {
  // Convert the float to a string, as floats can be a pain to add together
  let time = arr[i].toString().split('.');
  // Tally up the hours.
  hoursTally += parseInt(time[0]);
  // Tally up the minutes;
  let minute = time[1];
  if(minute) {
    // We do this so we add 20 minutes, not 2 minutes
    if(minute.length < 2) {
      minutesTally += parseInt(minute) * 10;
    } else {
      minutesTally += parseInt(minute);
    }
    // If the minutesTally is greater than 60, add an hour to hours
    if(minutesTally >= 60) {
      hoursTally++;
      // Set the minutesTally to be the remainder after dividing by 60
      minutesTally = minutesTally % 60;
    }
  }
  console.log(hoursTally + "." + minutesTally);
}


uh ... in ogni caso .. 2.2 + 1.1 IS 3.3 con il significato di 3h30min
eagle275

1
toString().split('.');- argh, davvero?
user253751

3
@ eagle275 Ma il numero 1.1 + 2.2 non è 3.3 quando i numeri sono in virgola mobile. Sono 3.3000000000000001 o 3.29999999999999 o qualcosa del genere. Questo perché 1.1 non è realmente 1.1 e 2.2 non è realmente 2.2 e 3.3 non è realmente 3.3 in virgola mobile.
user253751

non iniziare a dividere i peli - non sceglierei questo strano schema di numeri dall'inizio ... quando ho iniziato la programmazione avrei probabilmente convertito tutto in minuti (o secondi se necessario) - riconvertirli per la visualizzazione .. ma al giorno d'oggi DateTime mi regna negli occhi
eagle275,

1

const arr = [0.15, 0.2, 3.45, 0.4, 2, 0.3, 5.2, 1, 1.4, 1.1, 2.4, 1, 3.4]

const reducer = (acc, curr) => {
  acc = `${acc}`.split('.').length > 1 ? acc : `${acc}.0`;
  curr = `${curr}`.split('.').length > 1 ? curr : `${curr}.0`;
  let hours = parseStrAndSum(true, `${acc}`.split('.')[0], `${curr}`.split('.')[0]);
  let minute = parseStrAndSum(false, `${acc}`.split('.')[1], `${curr}`.split('.')[1]);
  hours = parseInt(hours / 1) + parseInt(minute / 60);
  minute = minute % 60;
  console.log(`${acc} + ${curr} = ${hours}.${minute}`);
  return `${hours}.${minute}`;
}

const parseStrAndSum = (is_hours, ...strs) => {
  let result = 0;
  strs.forEach(function(number) {
    if (!is_hours) {
      number = number.length == 1 ? `${number}0` : number;
    }
    result += parseInt(number);
  });
  return result;
};

arr.reduce(reducer);


1
@lan 0.15 + 0.2 Why =0.17? Deve essere0.35
user12916796

1
A quel tempo pensavo che il decimale 0,2 fosse uguale a due minuti anziché a 20 minuti. Ora che ho modificato il codice, conferma di nuovo.
Ian

1

Il modo standard per ridurre un modulo è aggiungere il deficit dopo l'aggiunta se si è verificato un overflow. Questo è il modo in cui faresti l'aggiunta di BCD usando ad esempio l'hardware binario.

Anche se puoi fare l'aggiunta usando i float in questo modo, c'è una domanda se dovresti . I problemi con l'utilizzo dei numeri float per operare su quelle che sono sostanzialmente quantità intere sono ben noti e non li ripeterò qui.

Normalmente, l'aggiunta sulle frazioni float funziona implicitamente con un modulo di 1,0, un overflow della frazione incrementa la parte intera. Se interpretiamo il punto come separatore hours.minutes, vogliamo che la frazione trabocchi a 0,6. Ciò richiede che il disavanzo di 0,4 sia aggiunto al momento opportuno.

function add_float_hours_minutes(a, b)
{
  var trial_sum = a+b;
  // did an actual overflow occur, has the integer part incremented?
  if ( Math.floor(trial_sum) > Math.floor(a)+Math.floor(b) )  {
    trial_sum += 0.4;
  }
  // has the fractional part exceeded its allotted 60 minutes?
  if ( trial_sum-Math.floor(trial_sum) >= 0.6)  {
    trial_sum += 0.4;
  }
  return trial_sum;
}

var arr = [0.15, 0.2, 3.45, 0.4, 2, 0.3, 5.2, 1, 1.4, 1.1, 2.4, 1, 3.4]

var sum = 0.0;

arr.forEach(function(item) {
  console.log(sum);
  // sum += parseFloat(item);
  sum = add_float_hours_minutes(sum, parseFloat(item));
});

che produce il seguente output, corretto all'interno di un arrotondamento a ciò che l'OP ha richiesto

0
0.15
0.35
4.2
5.000000000000001
7.000000000000001
7.300000000000001
12.5
13.5
15.3
16.400000000000002
19.2
20.2

Ho lasciato la formattazione finale a due cifre decimali come esercizio per il lettore. Le 15 cifre finali in tutta la loro gloria illustrano parte del perché l'uso dei float non è il modo migliore per farlo.

Inoltre, considera cosa succede quando vuoi includere secondi o giorni. I due modi piuttosto più standard di float secondi, o una tupla di numeri interi, sembrano improvvisamente molto più sensati.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.