Bagaimana cara menjumlahkan angka float dalam format jam dan menit?

14

Bagaimana cara menjumlahkan angka float dalam format jam dan menit?

Itu jika menghitung dua angka seperti 0.35+3.45itu maka itu harus = 4.20, bukan3.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);
});

Hasilnya harus seperti ini:

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
pengguna12916796
sumber
1
Bagaimana Anda mendefinisikan logika?
Juhil Somaiya
6
Jadi apakah 0,2 mewakili 20 menit, atau 0,2 jam? apakah 1.0 berarti satu jam, atau apakah 0.6 berarti satu jam?
Spangle
2
Maaf, jadi 1,0 berarti 1 jam dan 0 menit? Apa artinya 0,6 dan 0,7 misalnya?
Spangle
2
Anda akan menemukan jawabannya di sini: codereview.stackexchange.com/a/109478 . Konversikan angka Anda menjadi string, dan dalam fungsinya gunakan "."untuk membagi string itu, bukan ":".
Gerardo Furtado
6
Anda menyulitkan diri sendiri dengan logika seperti itu. 0.5jam harus 30 menit, yang lain benar-benar tidak rasional. Saya harap ini bukan untuk klien dalam kehidupan nyata .. Entah bekerja dengan nilai float dari unit waktu yang tetap atau Anda memisahkan jam / menit. Sesimpel itu.
Kaddath

Jawaban:

7

Cara terbaik untuk menangani format data "gila" seperti itu adalah dengan mengonversikannya menjadi format yang waras dan mudah diproses, lakukan apa pun yang perlu Anda lakukan dengan menggunakan data yang dikonversi dan akhirnya (jika Anda benar-benar harus) mengonversi hasilnya kembali ke format aslinya.

(Tentu saja, solusi nyata adalah berhenti menggunakan representasi waktu gila seperti itu sepenuhnya. Tapi itu tidak selalu praktis, misalnya karena Anda perlu berinteraksi dengan sistem lama yang tidak dapat Anda ubah.)

Dalam kasus Anda, format waras yang cocok untuk data Anda akan menjadi misalnya jumlah menit yang tidak terpisahkan. Setelah mengonversi data ke format ini, Anda dapat melakukan aritmatika biasa.

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

Perhatikan bahwa kode konversi di atas pertama kali mengalikan float input dengan 100 dan membulatkannya menjadi integer sebelum memisahkan bagian jam dan menit. Ini harus secara kuat menangani input dengan kemungkinan kesalahan pembulatan sambil menghindari kebutuhan untuk merumuskan input.

Alasan untuk lebih berhati-hati di sini adalah karena angka 0,01 = 1/100 (dan sebagian besar kelipatannya) sebenarnya tidak persis mewakili dalam format titik-mengambang biner yang digunakan oleh JavaScript. Dengan demikian Anda tidak dapat benar-benar memiliki nomor JS yang akan sama persis 0,01 - yang terbaik yang dapat Anda miliki adalah angka yang sangat dekat sehingga mengonversinya menjadi string akan secara otomatis menyembunyikan kesalahan. Tetapi kesalahan pembulatan masih ada, dan dapat menggigit Anda jika Anda mencoba melakukan hal-hal seperti membandingkan angka-angka tersebut dengan ambang yang tepat. Berikut ini adalah demonstrasi yang bagus dan sederhana:

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

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

Anda dapat menggunakan logika ini setelah melakukan penambahan aritmatika sederhana, ini akan mengubah jumlah menjadi format waktu

Mari kita bertahan
sumber
2
Kenapa math.floor(sample/1)bukannya adil math.floor(sample)?
selbie
2
@selbie, hanya karena saya pertama menambahkan saja sample / 1, saya pikir JS akan lantai itu sendiri karena tidak ada desimal di pembagi (seperti dalam sample / 1.0, cukup lama sejak menggunakan JS;), Tapi itu tidak jadi saya dengan cepat googled dan menambahkan floor, lupa menghapusnya. Bagaimanapun terima kasih telah menunjukkan bahwa
Letsintegreat
2
Anda harus menyebutkan bahwa ini perlu dilakukan untuk setiap elemen arrbukan pada hasilnya. Misalnya, jika arr = [1.5, 2.5]hasilnya akan 4bukan4.4
Titus
1
@Itus, Ya, saya melewatkan bagian itu, jadi kita perlu mengambil jam dan menit dari setiap elemen, lalu menambahkannya secara terpisah, maka hanya pendekatan saya yang akan berguna.
Letsintegreat
4

Ini dia, implementasi dalam Javascript ES6. Saya melingkari setiap elemen, dan membagi jam, menit demi menit ., memeriksa apakah menit tidak ada, menetapkan 0 menghitung total jam dan menit dan menerapkan perhitungan yang diperlukan.

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
sumber
1
Tidak akan berfungsi sebagaimana mestinya, misalnya 0,3 hanya akan dihitung sebagai tiga menit, bukan 30 menit dengan solusi Anda.
Spangle
3

Anda perlu mengubah semuanya menjadi jam atau menit, lalu menghitung.

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
sumber
2

Anda harus mengonversikannya menjadi jam dan menit terlebih dahulu

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
sumber
2
Sayangnya tidak ada yang memperhatikan ini pada awalnya. Dan meskipun mungkin benar, di mana pembelajaran datang? Salin dan tempel dari OP solusi Anda banyak membantu dalam jangka pendek. Tapi itu adalah pertanyaan yang cerdas yang pantas mendapatkan penjelasan yang cerdas.
GetSet
2

Bolehkah saya tahu mengapa Anda ingin hasil pertama menjadi 0 ? Di bawah ini adalah cara untuk melakukan ini dengan satu loop. Komentar ada dalam kode untuk dijelaskan. Dengan setiap loop kami menambahkan menit, dan jika mereka lebih besar dari 60, kami menambahkan satu jam, dan kemudian gunakan % 60 untuk mendapatkan menit yang tersisa. Mengapung bisa menyebalkan,

misal 1.1 + 2.2 === 3.3 salah

Jadi saya mengonversi angka menjadi string, dan kemudian kembali ke 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);
}

Kida-kida
sumber
uh .. tentu saja .. 2.2 + 1.1 IS 3.3 dengan arti 3h30menit
eagle275
1
toString().split('.');- argh, benarkah?
user253751
3
@ eagle275 Tapi angka 1.1 + 2.2 bukan 3.3 ketika angka-angka itu adalah floating-point. Ini 3.3000000000000001 atau 3,29999999999999 atau sesuatu. Itu karena 1.1 tidak benar-benar 1.1 dan 2.2 tidak benar-benar 2.2 dan 3.3 tidak benar-benar 3.3 di floating-point.
user253751
jangan mulai membelah rambut - saya tidak akan memilih skema angka aneh ini sejak awal ... ketika saya memulai pemrograman saya mungkin akan mengubah semuanya menjadi beberapa menit (atau detik jika perlu) - mengubahnya kembali untuk ditampilkan .. tapi sekarang aturan DateTime di mataku
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
sumber
1
@lan 0.15 + 0.2 Kenapa =0.17? Harus0.35
user12916796
1
Pada saat itu saya berpikir bahwa desimal 0,2 sama dengan dua menit, bukan 20 menit. Sekarang saya telah menyesuaikan kode, mohon konfirmasi kembali.
Ian
1

Cara standar untuk mengurangi modulus adalah dengan menambahkan defisit setelah penambahan jika terjadi overflow. Ini adalah bagaimana Anda akan melakukan penambahan BCD menggunakan perangkat keras biner misalnya.

Meskipun Anda bisa melakukan penambahan menggunakan pelampung dengan cara ini, ada pertanyaan apakah Anda harus melakukannya . Masalah dengan menggunakan angka float untuk beroperasi pada apa yang pada dasarnya jumlah integer sudah diketahui, dan saya tidak akan mengulanginya di sini.

Biasanya, penambahan pada pecahan float bekerja secara implisit dengan modulus 1,0, melimpah keluar dari fraksi menambah bagian bilangan bulat. Jika kita mengartikan titik sebagai pemisah Hours.minutes, maka kita ingin fraksi meluap pada 0,6. Ini membutuhkan defisit 0,4 untuk ditambahkan pada waktu yang tepat.

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

yang menghasilkan output berikut, koreksi ke dalam pembulatan ke apa yang diminta OP

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

Saya telah meninggalkan pemformatan terakhir ke dua tempat desimal sebagai latihan untuk pembaca. Ke 15 digit tambahan dalam semua kemuliaan menggambarkan bagian mengapa menggunakan pelampung bukanlah cara terbaik untuk melakukan ini.

Juga, pertimbangkan apa yang terjadi ketika Anda ingin memasukkan detik, atau hari. Dua cara yang lebih standar baik float detik, atau tuple of integer, tiba-tiba terlihat jauh lebih masuk akal.

Neil_UK
sumber