如何将小时和分钟格式的浮点数求和?


14

如何将小时和分钟格式的浮点数求和?

那些。如果计算两个这样的数字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.0表示一个小时,还是0.6表示一个小时?
Spangle

2
抱歉,所以1.0表示1小时0分钟?例如0.6和0.7是什么意思?
Spangle

2
您将在这里找到答案:codereview.stackexchange.com/a/109478。将您的数字转换为字符串,然后在函数中使用"."拆分这些字符串,而不是":"
Gerardo Furtado

6
您会使这种逻辑复杂化。0.5小时应该是30分钟,其他任何事情都是完全不合理的。我希望它不是现实生活中的客户。要么以固定时间单位的浮点值工作,要么分开小时/分钟。就如此容易。
卡达斯

Answers:


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,然后将其四舍五入为整数,然后再将小时和分钟部分分开。这应该稳健地处理可能存在舍入错误的输入,同时避免对输入进行分类。

这里要格外小心的原因是,在JavaScript使用的二进制浮点格式中,数字0.01 = 1/100(及其大多数倍数)实际上无法准确表示。因此,实际上您不能拥有一个完全等于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)
selbie

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分钟。
Spangle

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,则增加一个小时,然后使用%60来获取剩余的分钟数。浮动可能会让人痛苦,

例如1.1 + 2.2 === 3.3为假

因此,我将数字转换为字符串,然后转换为整数。

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 IS 3.3的含义为3h30min
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在我眼中
占主导地位

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等于2分钟而不是20分钟。现在,我已经调整了代码,请再次确认。
伊恩

1

减少模量的标准方法是在引起溢出的情况下,在相加后相加缺陷。例如,这就是使用二进制硬件进行BCD添加的方式。

尽管您可以通过这种方式使用浮点数进行加法运算,但是仍然存在一个问题,即是否应该使用浮点数。使用浮点数对基本上是整数的数字进行运算的问题是众所周知的,在此我将不对其进行哈希处理。

通常,浮点数的加法会隐式地以1.0的模数工作,分数的溢出会使整数部分递增。如果将点解释为hours.minutes分隔符,则我们希望分数在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

我将最终格式留给两位小数,以供读者练习。所有荣耀中的15个尾随数字都说明了为什么使用浮点数并不是做到这一点的最佳方法的部分原因。

另外,请考虑当您要包含秒或天时会发生什么。浮动秒数或整数元组的两种更为标准的方式突然显得更加明智。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.