시간과 분 형식의 부동 소수점 수를 합산하는 방법은 무엇입니까?


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 분 또는 1 시간의 0.2를 나타 냅니까? 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 분이되어야합니다. 다른 것은 완전히 비합리적입니다. 고객이 실제 생활에서 사용하지 않기를 바랍니다. 고정 시간 단위의 부동 소수점 값으로 작업하거나 시간 / 분을 분리합니다. 저것과 같이 쉬운.
Kaddath

답변:


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

위의 변환 코드는 먼저 입력 float에 100을 곱한 후 시간과 분을 분리하기 전에 정수로 반올림합니다. 이것은 입력을 스트링화할 필요없이 반올림 오류가 발생할 수있는 입력을 강력하게 처리해야합니다.

여기에서 특별한주의를 기울여야하는 이유는 숫자 0.01 = 1/100 (및 대부분의 배수)이 실제로 JavaScript에서 사용되는 이진 부동 소수점 형식으로 정확하게 표현할 수 없기 때문 입니다. 따라서 당신이 실제로 일 것 JS 번호 가질 수 없습니다 정확히 0.01 같음 - 당신이 할 수있는 최선은 너무 가까이 문자열로의 변환은 자동으로 오류를 숨길 것입니다 숫자입니다. 그러나 반올림 오류는 여전히 존재하며 이러한 숫자를 정확한 임계 값과 비교하는 것과 같은 일을 시도하면 물릴 수 있습니다. 다음은 훌륭하고 간단한 데모입니다.

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가 자체를 바닥 화하지만 (JS를 sample / 1.0사용한 이후 꽤 긴 시간); floor제거하는 것을 잊었습니다. 어쨌든 그것을 지적 해 주셔서 감사합니다
Letsintegreat

2
arr결과 가 아닌 모든 요소에 대해이 작업을 수행해야합니다 . 예를 들어, arr = [1.5, 2.5]결과는 것 4대신에4.4
디도

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은 거짓

그래서 숫자를 문자열로 변환 한 다음 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 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 규칙은 내 눈에
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
그 당시 저는 10 진수 0.2가 20 분이 아니라 2 분이라고 생각했습니다. 코드를 조정 했으므로 다시 확인하십시오.
이안

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 자리 숫자는 플로트를 사용하는 것이 가장 좋은 방법이 아닌 이유를 보여줍니다.

또한 초 또는 일을 포함 시키려고 할 때 어떤 일이 발생하는지 고려하십시오. float 초 또는 정수 튜플의 두 가지보다 표준적인 방법은 갑자기 훨씬 더 합리적으로 보입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.