Wie summiere ich Float-Zahlen im Format Stunden und Minuten?

14

Wie summiere ich Float-Zahlen im Format Stunden und Minuten?

Jene. Wenn Sie zwei solcher Zahlen berechnen 0.35+3.45, sollte dies = sein 4.20, nicht3.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);
});

Das Ergebnis sollte folgendermaßen aussehen:

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
user12916796
quelle
1
Wie definieren Sie die Logik?
Juhil Somaiya
6
Entspricht 0,2 also 20 Minuten oder 0,2 Stunden? bedeutet 1,0 eine Stunde oder bedeutet 0,6 eine Stunde?
Spangle
2
Entschuldigung, 1.0 bedeutet also 1 Stunde und 0 Minuten. Was bedeuten zum Beispiel 0,6 und 0,7?
Spangle
2
Eine Antwort finden Sie hier: codereview.stackexchange.com/a/109478 . Konvertieren Sie Ihre Zahlen in Zeichenfolgen und "."teilen Sie diese Zeichenfolgen in der Funktion nicht ":".
Gerardo Furtado
6
Sie erschweren sich mit einer solchen Logik. 0.5Stunden sollten 30 Minuten sein, alles andere ist völlig irrational. Ich hoffe, es ist nichts für einen Kunden im wirklichen Leben. Entweder arbeiten Sie mit Float-Werten einer festen Zeiteinheit oder Sie trennen Stunden / Minuten. So einfach ist das.
Kaddath

Antworten:

7

Der beste Weg, mit solchen "verrückten" Datenformaten umzugehen, besteht darin, sie zuerst in ein vernünftiges und leicht verarbeitbares Format zu konvertieren, mit den konvertierten Daten alles zu tun, was Sie tun müssen, und schließlich (wenn Sie es unbedingt müssen) die Ergebnisse wieder in zu konvertieren das ursprüngliche Format.

(Natürlich besteht die wirkliche Lösung darin, die Verwendung solcher verrückten Zeitdarstellungen gänzlich einzustellen. Dies ist jedoch nicht immer praktisch, z. B. weil Sie eine Schnittstelle zu einem Legacy-System herstellen müssen, das Sie nicht ändern können.)

In Ihrem Fall wäre ein geeignetes vernünftiges Format für Ihre Daten beispielsweise eine ganzzahlige Anzahl von Minuten. Sobald Sie Ihre Daten in dieses Format konvertiert haben, können Sie gewöhnliche Arithmetik ausführen.

// 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) )

Beachten Sie, dass der obige Konvertierungscode zuerst den Eingabe-Float mit 100 multipliziert und auf eine Ganzzahl rundet, bevor die Stunden- und Minutenteile getrennt werden. Dies sollte Eingaben mit möglichen Rundungsfehlern robust behandeln und gleichzeitig die Notwendigkeit vermeiden, die Eingaben zu stringifizieren.

Der Grund für besondere Vorsicht ist, dass die Zahl 0,01 = 1/100 (und die meisten ihrer Vielfachen) im von JavaScript verwendeten binären Gleitkommaformat nicht genau darstellbar ist . Daher können Sie keine JS-Nummer haben, die genau 0,01 entspricht. Das Beste, was Sie haben können, ist eine Nummer, die so nahe beieinander liegt, dass beim Konvertieren in eine Zeichenfolge der Fehler automatisch ausgeblendet wird. Der Rundungsfehler ist jedoch weiterhin vorhanden und kann Sie beißen, wenn Sie beispielsweise versuchen, solche Zahlen mit einem genauen Schwellenwert zu vergleichen. Hier ist eine schöne und einfache Demonstration:

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

Ilmari Karonen
quelle
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)

Sie können diese Logik verwenden, nachdem Sie eine einfache arithmetische Addition durchgeführt haben. Dadurch wird die Summe in das Zeitformat konvertiert

Letsintegreat
quelle
2
Warum math.floor(sample/1)statt nur math.floor(sample)?
Selbie
2
@selbie, einfach weil ich zuerst nur hinzugefügt habe sample / 1, ich denke, JS wird es selbst auf den Boden legen, weil es keine Dezimalstelle im Divisor gab (wie in sample / 1.0, seit ziemlich langer Zeit JS verwendet;), aber es hat nicht so schnell gegoogelt und hinzugefügt floorhabe vergessen, das zu entfernen. Trotzdem danke, dass
du
2
Sie sollten erwähnen, dass dies für jedes Element durchgeführt werden muss, das arrnicht im Ergebnis enthalten ist. Zum Beispiel, wenn arr = [1.5, 2.5]das Ergebnis 4anstelle von4.4
Titus
1
@Titus, Ja, ich habe diesen Teil verpasst, daher müssen wir Stunden und Minuten von jedem einzelnen Element abrufen und sie dann separat hinzufügen. Dann ist nur mein Ansatz hilfreich.
Letsintegreat
4

Los geht's, Implementierung in Javascript ES6. Ich habe jedes Element durchgeschleift und Stunden, Minuten nach geteilt ., überprüft, ob Minuten nicht vorhanden sind, andernfalls 0 zugewiesen, die Gesamtzahl der Stunden und Minuten gezählt und die erforderliche Berechnung angewendet.

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);

onecompileman
quelle
1
Funktioniert nicht so wie es ist, z. B. wird 0,3 nur als drei Minuten gezählt, nicht als 30 Minuten mit Ihrer Lösung.
Spangle
3

Sie müssen alles entweder in Stunden oder in Minuten umrechnen und dann rechnen.

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!
Kaustubh - KAY
quelle
2

Sie müssen sie zuerst in Stunden und Minuten konvertieren

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]));

Akash Shrivastava
quelle
2
Leider hat das zunächst niemand bemerkt. Und obwohl wahrscheinlich richtig, wo kommt das Lernen ins Spiel? Das Kopieren und Einfügen Ihrer Lösung aus dem OP hilft natürlich kurzfristig sehr. Aber es war eine intelligente Frage, die eine intelligente Erklärung verdient.
GetSet
2

Darf ich wissen, warum das erste Ergebnis 0 sein soll ? Unten finden Sie eine Möglichkeit, dies mit einer einzelnen Schleife zu tun. Kommentare sind im Code zu erklären. Mit jeder Schleife addieren wir die Minuten. Wenn sie größer als 60 sind, addieren wir eine Stunde und verwenden dann % 60 , um die verbleibenden Minuten zu erhalten. Schwimmer können ein Schmerz sein, mit dem man arbeiten muss,

zB 1.1 + 2.2 === 3.3 ist falsch

Also habe ich die Zahlen in eine Zeichenfolge konvertiert und dann zurück in eine 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);
}

Flitter
quelle
uh .. auf jeden Fall .. 2.2 + 1.1 IS 3.3 mit der Bedeutung von 3h30min
eagle275
1
toString().split('.');- Argh, wirklich?
user253751
3
@ eagle275 Aber die Zahl 1.1 + 2.2 ist nicht 3.3, wenn die Zahlen Gleitkommazahlen sind. Es ist 3.3000000000000001 oder 3.29999999999999 oder so. Das liegt daran, dass 1.1 nicht wirklich 1.1 ist und 2.2 nicht wirklich 2.2 und 3.3 nicht wirklich 3.3 im Gleitkomma ist.
user253751
Fangen Sie nicht an, Haare zu spalten - ich würde dieses seltsame Zahlenschema nicht von Anfang an wählen ... Als ich mit dem Programmieren anfing, hätte ich wahrscheinlich alles auf Minuten (oder Sekunden, falls erforderlich) umgerechnet - konvertieren Sie sie für die Anzeige zurück. aber heutzutage regiert DateTime in meinen Augen
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);

Ian
quelle
1
@lan 0.15 + 0.2 Warum =0.17? Muss sein0.35
user12916796
1
Zu dieser Zeit dachte ich, dass die Dezimalstelle 0,2 zwei Minuten statt 20 Minuten entspricht. Nachdem ich den Code angepasst habe, bestätigen Sie bitte erneut.
Ian
1

Die Standardmethode zum Reduzieren eines Moduls besteht darin, das Defizit nach dem Hinzufügen zu addieren, wenn ein Überlauf verursacht wurde. So würden Sie die BCD-Addition beispielsweise mit binärer Hardware durchführen.

Obwohl Sie können zusätzlich mit Schwimmern auf diese Weise zu tun, gibt es eine Frage, ob man sollte . Die Probleme bei der Verwendung von Gleitkommazahlen für Operationen mit im Wesentlichen ganzzahligen Mengen sind bekannt, und ich werde sie hier nicht erneut aufbereiten.

Normalerweise arbeitet die Addition von Float-Brüchen implizit mit einem Modul von 1,0, ein Überlauf aus dem Bruch erhöht den ganzzahligen Teil. Wenn wir den Punkt als Stunden.Minuten-Trennzeichen interpretieren, möchten wir, dass der Bruch bei 0,6 überläuft. Dazu muss das Defizit von 0,4 zum richtigen Zeitpunkt addiert werden.

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));
});

Dies erzeugt die folgende Ausgabe, die innerhalb einer Rundung auf das korrigiert wird, was das OP verlangt hat

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

Ich habe die endgültige Formatierung als Übung für den Leser auf zwei Dezimalstellen belassen. Die 15 nachgestellten Ziffern in all ihrer Pracht veranschaulichen einen Teil dessen, warum die Verwendung von Floats nicht der beste Weg ist, dies zu tun.

Überlegen Sie auch, was passiert, wenn Sie Sekunden oder Tage einschließen möchten. Die beiden eher üblichen Methoden, entweder Sekunden oder ein Tupel von ganzen Zahlen zu schweben, sehen plötzlich viel sinnvoller aus.

Neil_UK
quelle