時間と分の形式で浮動小数点数を合計するにはどうすればよいですか?


14

時間と分の形式で浮動小数点数を合計するにはどうすればよいですか?

それら。そのような数値を2つ計算する場合0.35+3.45は、= 4.20ではなく、3.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);
});

結果は次のようになります。

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
ロジックをどのように定義しますか?
Juhil Somaiya

6
0.2は20分、つまり0.2は1時間を表すのでしょうか?1.0は1時間を意味しますか、または0.6は1時間を意味しますか?
スパングル

2
1.0は1時間0分を意味しますか?たとえば、0.6と0.7はどういう意味ですか?
スパングル

2
答えはここにあります:codereview.stackexchange.com/a/109478。数値を文字列に変換し、関数で"."それらを分割するために使用し":"ます。
Gerardo Furtado

6
あなたはそのような論理であなた自身を複雑にします。0.5時間は30分である必要があります。それ以外は完全に不合理です。実生活のクライアント向けではないことを願っています。固定時間単位の浮動小数点値を処理するか、時間/分を分離します。それと同じくらい簡単です。
カダス

回答:


7

そのような「クレイジーな」データ形式を処理する最良の方法は、まずそれらを健全で簡単に処理できる形式に変換し、変換されたデータを使用して必要なことをすべて実行し、最後に(どうしても必要な場合)結果を元のフォーマット。

(もちろん、本当の解決策は、そのようなクレイジーな時間表現の使用を完全に停止することです。しかし、たとえば、変更できないレガシーシステムとやり取りする必要があるためなど)、それが常に実用的であるとは限りません。

あなたの場合、あなたのデータに適切な正気なフォーマットは、例えば分数です。データをこの形式に変換したら、通常の演算を実行できます。

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

上記の変換コードは、最初に入力浮動小数点数を100で乗算し、それを整数に丸めてから時間と分を分離することに注意してください。これは、入力を文字列化する必要を回避しながら、丸め誤差の可能性がある入力を確実に処理する必要があります。

ここで特に注意を払う理由は、0.01 = 1/100(およびそのほとんどの倍数)は、JavaScriptで使用される2進浮動小数点形式では実際には正確に表現できないためです。したがって、実際には、完全に0.01 等しいJS番号を使用することはできません。可能な限り最良の数値は、文字列に変換するとエラーが自動的に非表示になるほど近い数値です。しかし、丸め誤差はまだそこにあり、そのような数値を正確なしきい値と比較するようなことを行おうとすると、あなたを噛む可能性があります。以下は、素晴らしくシンプルなデモです。

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)

単純な算術加算を行った後、このロジックを使用できます。これにより、合計が時間形式に変換されます


2
なぜmath.floor(sample/1)単にではなくmath.floor(sample)
セルビー

2
@selbie、私が最初に追加したというだけの理由sample / 1で、除数に小数がなかったのでJSはそれ自体をフロアリングしますが(のようにsample / 1.0、JSを使用してからかなり長い間);しかし、そうではなかったので、すぐにググって追加しましたfloor、削除するのを忘れました。とにかくそれを指摘してくれてありがとう
Letsintegreat

2
これはarr、結果ではなく、すべての要素に対して実行する必要があることを言及する必要があります。たとえばarr = [1.5, 2.5]、結果が次のよう4になる場合4.4
Titus

1
@Titus、はい、その部分を逃したので、すべての要素から時間と分をフェッチしてから、それらを個別に追加する必要があります。そうすると、私のアプローチのみが役立ちます。
Letsintegreat

4

さあ、JavaScript ES6での実装です。私は各要素をループし.、時間、分をで分割し、分が存在しないかどうかを確認します。それ以外の場合は、0を割り当て、時間と分を合計し、必要な計算を適用しました。

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
そのままでは機能しません。たとえば、0.3は、ソリューションでは30分ではなく、3分としてカウントされます。
スパングル

3

すべてを数時間または数分に変換してから計算する必要があります。

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

最初に時間と分に変換する必要があります

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
残念ながら、最初は誰もこれに気づきませんでした。そして、おそらく正しいのですが、学習はどこで行われますか?ソリューションのOPからコピーして貼り付けると、短期的には多くのことが役立ちます。しかし、それはインテリジェントな説明に値するインテリジェントな質問でした。
GetSet

2

最初の結果を0にしたいのはなぜですか?以下は、単一のループでこれを行う方法です。コメントは説明するコードにあります。各ループで分を追加し、それらが60より大きい場合は1時間を追加してから、%60を使用して残りの分を取得します。フロートは作業が面倒な場合があります。

例:1.1 + 2.2 === 3.3はfalse

そのため、数値を文字列に変換してから、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);
}


ええと。どうしても.. 2.2 + 1.1は3時間
eagle275

1
toString().split('.');-ああ、本当ですか?
user253751

3
@ eagle275しかし、数値が浮動小数点の場合、数値1.1 + 2.2は3.3ではありません。3.3000000000000001または3.29999999999999などです。それは、浮動小数点では、1.1は実際には1.1ではなく、2.2は実際には2.2ではなく、3.3は実際には3.3ではないためです。
user253751

髪の毛を分割し始めないでください-最初からこの奇妙な番号スキームを選択しません...私がプログラミングを開始したとき、私はおそらくすべてを分(または必要に応じて秒)に変換していました-表示用に変換し直しました..しかし、最近ではDateTimeが私の目を支配しています
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 なんで=0.17?する必要があります0.35
user12916796

1
その時、私は小数点以下0.2は20分ではなく2分に等しいと思いました。コードを調整したので、もう一度確認してください。
イアン

1

モジュラスを低減する標準的な方法は、オーバーフローが発生した場合、加算の後に赤字を加算することです。これは、たとえばバイナリハードウェアを使用してBCDを追加する方法です。

この方法でフロートを使用して加算を実行できますが、を実行する必要があるかどうかという疑問があります。基本的に整数の数量であるものを操作するために浮動小数点数を使用することに関する問題はよく知られているので、ここではそれらを再度ハッシュすることはしません。

通常、浮動小数の加算は、係数1.0で暗黙的に機能し、小数からのオーバーフローは整数部分をインクリメントします。ポイントを時間と分の区切りとして解釈する場合、小数部を0.6でオーバーフローさせます。これには、適切な時点で0.4の赤字を追加する必要があります。

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

これは、次の出力を生成します。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

読者のための演習として、最終的なフォーマットを小数点以下2桁に残しました。栄光の中で最後の15桁は、フロートの使用がこれを行うための最良の方法ではない理由の一部を示しています。

また、秒数や日数を含めたい場合にどうなるかを検討してください。float秒、または整数のタプルのどちらかという2つの標準的な方法は、突然、より賢明に見えます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.