-
Notifications
You must be signed in to change notification settings - Fork 7k
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
Comments
Yeah, that seems like a bug. Caused by this, I assume: > moment.duration(2.3, 'h').asMilliseconds()
8279999.999999999 |
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. |
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. |
@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? |
Note how there was no division in my examples. |
Then may be we should use some lib (or corresponding code subset) like https://github.com/infusion/Fraction.js for this? |
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? |
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? |
Sorry I might be wrong but while playing around:
expected result:
Perhaps a way to fix this is to make all floating point numbers to integers. Then dividing by the multiplier afterwards ?
|
@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 |
oh im sorry you are right. I must have been too caught up. |
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. |
Yes, probably should be on this line https://github.com/moment/moment/blob/develop/src/lib/duration/get.js#L18 |
@afanasy This gives the exact number in Chrome console.
(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? |
did this get finished? |
@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. |
Is there an unwritten commitment to supporting durations to sub-millisecond accuracy? If there isn't, you could just round-off milliseconds in the this._milliseconds = absRound(+milliseconds +
seconds * 1e3 + // 1000
minutes * 6e4 + // 1000 * 60
hours * 36e5 // 1000 * 60 * 60
); |
@ashsearle that's interesting. But I'm wondering if sub milliseconds could appear unintentionally as a by-product of some conversion 🤔 |
related to #4649 |
I understand that it's floating point problem, but can we work around this somehow, by not converting everything to milliseconds for instance?
The text was updated successfully, but these errors were encountered: