Bug 102742 - Calc function MOD returns wrong result with some arguments
Summary: Calc function MOD returns wrong result with some arguments
Status: RESOLVED FIXED
Alias: None
Product: LibreOffice
Classification: Unclassified
Component: Calc (show other bugs)
Version:
(earliest affected)
unspecified
Hardware: All All
: medium normal
Assignee: Winfried Donkers
URL:
Whiteboard: target:5.3.0
Keywords:
Depends on:
Blocks:
 
Reported: 2016-09-28 14:12 UTC by Winfried Donkers
Modified: 2017-01-03 08:32 UTC (History)
5 users (show)

See Also:
Crash report or crash signature:


Attachments
Document showing problems (23.63 KB, application/vnd.oasis.opendocument.spreadsheet)
2016-09-28 14:21 UTC, Winfried Donkers
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Winfried Donkers 2016-09-28 14:12:42 UTC
User-Agent:       Mozilla/5.0 (X11; Linux x86_64; rv:49.0) Gecko/20100101 Firefox/49.0
Build Identifier: 5.1.3.2

E.g. =MOD(1,485*1000;5) returns 5, but should return 0.

Reproducible: Always

Steps to Reproduce:
1. enter =MOD(1,485*1000;5) in a cell

Actual Results:  
5





Reset User Profile?No
Comment 1 Winfried Donkers 2016-09-28 14:21:57 UTC
Created attachment 127702 [details]
Document showing problems

Attached document was based on function test document.
Changes are that the column 'Expected' contains the proper results that should be returned (when result is known).
Also some extra tests have been added.

Sheet 'precision issues' shows problems with floating point precision, where MOD doesn't produce correct results and where I can't see the cause of the problem as the values are less than the maximum integer value for double precision IEEE754 numbers.

Fix currently produces correct results for all tests in sheet2 that have a white background in column 'Expected'.
Comment 2 Winfried Donkers 2016-09-28 14:26:50 UTC
(In reply to Winfried Donkers from comment #0)
> Actual Results:  
> 5
> 
> 
> 
> 
> 
> Reset User Profile?No

In the empty lines read
 Expected Results:
 0
Comment 3 Regina Henschel 2016-09-29 12:26:41 UTC
Apache OpenOffice has the same code in ScMod in interpr2.cxx and it returns the correct values for =MOD((3^31)-1;14), =MOD((3^31)-2;14) and =MOD((3^31)-1;14). Therefore the problem is not directly in that method. I wonder where LibreOffice is different to Apache OpenOffice.

The if-part, which handles pure integer numbers should be able to handle values up to 53 bit correctly. Removing that part is not the solution to fix the wrong results in the 3^31 cases.

The case =MOD(1.485*1000;5) is a rounding problem. 1.485*1000 does not result in a pure integer, and so it goes to the else-part. If you use the direct integer =MOD(1485;5), the result is correct. So it will not help to remove that part, which handles pure integer numbers.
Comment 4 Winfried Donkers 2016-09-29 13:09:13 UTC
(In reply to Regina Henschel from comment #3)

Regina, thank you for your input.

The cause is not (only) in the ScMod() function, it is in ::rtl::math::approxXxxx() functions (as well) as not all these go up to 53 bit values. 
The 3^31 cases are caused by these math functions, some tests show that they can be fixed there. AOO probably has different code in e.g. approxEqual(x, y).

WRT MOD(1.485*1000;5), the else part returns the correct result.

WRT the if part in ScMod(): it uses floor(), where ::rtl::math::approxFloor() is used in the else part. Whe retaining the if part I would at least unify the use of floor functions.
Comment 5 Regina Henschel 2016-09-29 13:42:18 UTC
I had written the if-part to catch those values, which are true integers and very large, and handle them with fmod. The related issue is https://bz.apache.org/ooo/show_bug.cgi?id=59153.

Do you have a build to test, whether in LO in case =MOD((3^31)-1;14) the if-part is really used?

Perhaps Eike has an idea, why the very same code works in AOO and not in LO. I refer to the cases =MOD((3^31)-1;14). 

The problem MOD(1.485*1000;5) is different.
Comment 6 Regina Henschel 2016-09-29 16:17:58 UTC
In a new document I cannot reproduce the "=MOD((3^31)-1;14) results 0"-error. I always get result 2 using LO4.2, LO4.5, LO5.2, LO5.3, all in Windows7 32bit.
Comment 7 Winfried Donkers 2016-09-30 06:02:03 UTC
(In reply to Regina Henschel from comment #6)
> In a new document I cannot reproduce the "=MOD((3^31)-1;14) results
> 0"-error. I always get result 2 using LO4.2, LO4.5, LO5.2, LO5.3, all in
> Windows7 32bit.

Regina, I will not contradict your findings. 
I have not submitted a definite fix yet, there are currently only ongoing investigations. What I e.g. did was remove all extra code and just retain the pure mathematical definition of a modulo function to be able to test that at least that is working 100%, because currently http://bugs.documentfoundation.org/attachment.cgi?id=127702 opened in version 5.2 or 5.3 show unexpected results in Sheet2, cells A9..A13, (A16,) A20, A23.
Comment 8 Luke 2016-10-01 19:50:39 UTC
If there is no easy fix to make the interpreter support large number, we should return an error like '#NUM!' when values overflow.

=MOD((3^31),14)=#NUM!

Is much better than an erroneous result. In your workbook A16 and A28:A31, expected value should be #NUM!.
Comment 9 Winfried Donkers 2016-10-05 15:36:21 UTC
(In reply to Luke from comment #8)
> If there is no easy fix to make the interpreter support large number, we
> should return an error like '#NUM!' when values overflow.

In case of overflow, Calc returns an error.
But it is not always possible to represent a value exactly in a binary value, for fractional values, but also for large values. In these cases a binary value as close as possible to the decimal value is used. This is general behaviour for all numeric operations in Calc (and in other applications).
Making MOD() return an error -apart from the problem to determine _if_ the result is incorrect or inaccurate- would be inconsistent with the return values of the other numeric operations in Calc.

Calc uses IEE754 double precision values, which is 64bit.
Comment 10 Luke 2016-10-05 18:35:51 UTC
(In reply to Winfried Donkers from comment #9)

> Making MOD() return an error -apart from the problem to determine _if_ the
> result is incorrect or inaccurate- would be inconsistent with the return
> values of the other numeric operations in Calc.
> 
> Calc uses IEE754 double precision values, which is 64bit.

Then shouldn't we make them consistent and improve our overflow detection? I just tested '=MOD((3^31),14)' on Google Sheets, Excel, and WPS Sheets. They all return #NUM!. Returning an error is far superior than retuning the wrong result. We seem to have a huge range of numbers that generate invalid results. Calc doesn’t start correctly start warning the user of #NUM! until ‘=MOD((3^700),14).  


If our underlying data structure should be able to handle slightly larger calculations, it would be a nice enhancement to improve it. But if that’s all you plan to do to fix this bug, should I file a separate bug report on the more serious issue of retuning invalid results?
Comment 11 Winfried Donkers 2016-10-06 05:41:06 UTC
(In reply to Luke from comment #10)
> Then shouldn't we make them consistent and improve our overflow detection? I
> just tested '=MOD((3^31),14)' on Google Sheets, Excel, and WPS Sheets. They
> all return #NUM!. Returning an error is far superior than retuning the wrong
> result. We seem to have a huge range of numbers that generate invalid
> results. Calc doesn’t start correctly start warning the user of #NUM! until
> ‘=MOD((3^700),14).  

That #NUM! error was caused by Calc recognising an invalid result, i.e. a negative value. For direct integer values one could set a maximum value and return an error with numerator values above that limit, but:
-the problem also applied for fractional values, where no limit value can be defined;
-when the argument does not contain a value, but a reference, calculation or formula, it is not possible to determine if it is an integer value. MOD(1.485*1000,5) returns an incorrect result (before the fix) because of this.
 
 
> If our underlying data structure should be able to handle slightly larger
> calculations, it would be a nice enhancement to improve it. But if that’s
> all you plan to do to fix this bug, should I file a separate bug report on
> the more serious issue of retuning invalid results?

Yes, as it is a generic problem -and a very valid one- a separate bug report would be best.
Comment 12 Eike Rathke 2016-10-06 11:42:51 UTC
Fwiw, I can not reproduce a problem with =MOD((3^31),14) and the like, after hard recalc the results in rows 28 to 31 are 3,2,1,0 for me, in 5.1 and 5.2 and master.

The problem with =MOD(1.485*1000,5) is different because 1.485*1000 does not produce a real integer but 1484.9999999999998 because already 1.485 is not exactly representable but 1.4849999999999999 instead, so fmod(1484.9999999999998,5) returns 4.9999999999997726, which then for display is rounded to 5. Check with number format 0.00000000000000E+00 that displays 4.99999999999977E+00

Btw, always applying ROUND(...,12) to the results to test equality is wrong. Results like 2.27373675443232E-13 are not equal to 0 and in this context unexpected.
Comment 13 Commit Notification 2016-10-13 19:49:52 UTC
Winfried Donkers committed a patch related to this issue.
It has been pushed to "master":

http://cgit.freedesktop.org/libreoffice/core/commit/?id=d8257535e348fa0b0a5c269d1aafa710585421d6

tdf#102742 fix wrong results for MOD function

It will be available in 5.3.0.

The patch should be included in the daily builds available at
http://dev-builds.libreoffice.org/daily/ in the next 24-48 hours. More
information about daily builds can be found at:
http://wiki.documentfoundation.org/Testing_Daily_Builds

Affected users are encouraged to test the fix and report feedback.