Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2.3 hour duration gives 2h 17m (should be 2h 18m) #2978

Open
afanasy opened this issue Feb 22, 2016 · 20 comments
Open

2.3 hour duration gives 2h 17m (should be 2h 18m) #2978

afanasy opened this issue Feb 22, 2016 · 20 comments

Comments

@afanasy
Copy link
Contributor

afanasy commented Feb 22, 2016

//moment.js 2.11.2
moment.duration(0.3, 'h').minutes()
//18, as expected
moment.duration(2.3, 'h').minutes()
//17
//?!?

I understand that it's floating point problem, but can we work around this somehow, by not converting everything to milliseconds for instance?

@icambron icambron added the Bug label Feb 23, 2016
@icambron
Copy link
Member

Yeah, that seems like a bug. Caused by this, I assume:

> moment.duration(2.3, 'h').asMilliseconds()
8279999.999999999

@mattjohnsonpint
Copy link
Contributor

Oh JavaScript floating point numbers, you're so funny...

2.3 * 60 * 60 * 1000 === 8280000
60 * 60 * 1000 === 3600000
2.3 * 3600000 === 8279999.999999999

WAT.

@mattjohnsonpint
Copy link
Contributor

Actually I think this one may require some more detailed thought in how we apply large constants against multiplication and division. Probably not ideal for up-for-grabs, but if you think you have a good approach, still send a PR. Be sure to include a unit test with at least the above example.

@afanasy
Copy link
Contributor Author

afanasy commented Feb 24, 2016

@mj1856 Initially the approach I'd take is to store duration values with units and use them directly for conversions in .units() calls, w/o converting to ms.

//2.3 hours to minutes
//current 2.11.2 version
2.3 * 3600000 / 60000 === 137.99999999999997 //minutes - wrong
//proposal
2.3 * 60 === 138 //minutes - correct

but now I don't know, may be it will produce new errors with some other floating point numbers, is it really the problem with division and/or large constants?

@mattjohnsonpint
Copy link
Contributor

Note how there was no division in my examples.

@afanasy
Copy link
Contributor Author

afanasy commented Feb 24, 2016

Then may be we should use some lib (or corresponding code subset) like https://github.com/infusion/Fraction.js for this?

@icambron
Copy link
Member

Eh, floating point numbers are just painful in general. FWIW, Java behaves exactly like Matt's "WAT" example.

I propose we just change this line to

hours * 3600 * 1000

Does that suffice?

@mattjohnsonpint
Copy link
Contributor

Works for me. We should have a test for that as well, as I could see someone accidently putting it back during a future cleanup.

Are there other areas like this?

@mnt
Copy link

mnt commented Mar 1, 2016

Sorry I might be wrong but while playing around:
actual result:

4.1 * 3600 * 1000 === 14759999.999999998
4.17123 * 1000 * 60 * 60 === 15016428.000000002

expected result:

4.1 * 3600 * 1000 === 14760000
4.17123 * 1000 * 60 * 60 === 15016428

Perhaps a way to fix this is to make all floating point numbers to integers. Then dividing by the multiplier afterwards ?

(4.1 * 10 * 3600 * 1000) / 10 === 14760000
(4.17123 * 100000 * 1000 * 60 * 60) / 100000 === 15016428

@afanasy
Copy link
Contributor Author

afanasy commented Mar 1, 2016

@mnt Looks interesting, but doesn't work for me for

(4.17123 * 100000 * 1000 * 60 * 60) / 100000
//outputs 15016428.000000002 in Google Chrome console

but still may be it covers more cases

@mnt
Copy link

mnt commented Mar 1, 2016

oh im sorry you are right. I must have been too caught up.

@icambron
Copy link
Member

icambron commented Mar 2, 2016

Floating point numbers are the worst. Thinking about this a bit more, the real alternative is to just do some smarter rounding. Specifically, it's one thing to be off by 0.0000001, but it's another to round that down really far, as in the original issue, where we apparently rounded 17.999983333333333 down to 17.

@afanasy
Copy link
Contributor Author

afanasy commented Mar 2, 2016

Yes, probably should be on this line https://github.com/moment/moment/blob/develop/src/lib/duration/get.js#L18

afanasy added a commit to akura-co/moment that referenced this issue Mar 8, 2016
afanasy added a commit to akura-co/moment that referenced this issue Mar 8, 2016
ichernev pushed a commit that referenced this issue Apr 16, 2016
@arntj
Copy link

arntj commented May 24, 2016

@afanasy This gives the exact number in Chrome console.

parseInt(4.17123 * 100000 * 1000 * 60 * 60) / 100000

(It's like mnt suggests above, multiplying and dividing by a big factor, but with an extra conversion to int).

Doing something like this (or simply rounding off the milliseconds according to conventional rules) would possibly solve this issue, but perhaps that is a sub optimal solution from a performance or precision perspective?

@afanasy
Copy link
Contributor Author

afanasy commented May 25, 2016

@arntj yes, I think the @mnt suggestion - making milliseconds int out of float and then operating on it - is the best approach at the moment. While it probably will not fix all floating point issues, it will improve calculations for many similar cases. @mj1856 @icambron what do you think?

@marwahaha
Copy link
Member

did this get finished?

@afanasy
Copy link
Contributor Author

afanasy commented Nov 19, 2016

@marwahaha I'm not sure, because it's basically the floating point numbers standard behaviour. To fix this completely we will need to use some decimal lib to calculate things, like big.js internally.

@ashsearle
Copy link
Contributor

Is there an unwritten commitment to supporting durations to sub-millisecond accuracy?

If there isn't, you could just round-off milliseconds in the Duration constructor:

this._milliseconds = absRound(+milliseconds +
    seconds * 1e3 + // 1000
    minutes * 6e4 + // 1000 * 60
    hours * 36e5 // 1000 * 60 * 60
);

@afanasy
Copy link
Contributor Author

afanasy commented Dec 3, 2017

@ashsearle that's interesting. But I'm wondering if sub milliseconds could appear unintentionally as a by-product of some conversion 🤔

@marwahaha
Copy link
Member

related to #4649

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants