Fix overflow of Mod_Float for large number (#1522)

Fix issue: https://github.com/microsoft/Power-Fx/issues/1367

Problem this PR fix:
Start from 1e+16 to 1e+20, current mod_float return 0 for most of the
case (mostly wrong value). And overflow from 1e+21 or higher.
This PR use C# % function to do mod for the wrong/overflow issues.

Note on PA (Need to fix as well): mod result is always 0 from 1e18 (mod
123). So, it shared the same problem. mod result is wrong for > 1e17. It
did not return overflow error for 1e306 but return 0 (wrong value).

---------

Co-authored-by: Carlos Figueira <carlosff@microsoft.com>
This commit is contained in:
Hoa Nguyen 2023-05-25 08:08:49 -07:00 коммит произвёл GitHub
Родитель ed759c95ad
Коммит a8805417aa
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
5 изменённых файлов: 70 добавлений и 28 удалений

Просмотреть файл

@ -760,20 +760,14 @@ namespace Microsoft.PowerFx.Functions
return CommonErrors.DivByZeroError(irContext);
}
// r = a – N × floor(a/b)
double q = Math.Floor(arg0 / arg1);
if (IsInvalidDouble(q))
{
return CommonErrors.OverflowError(irContext);
}
double result = arg0 % arg1;
double result = arg0 - (arg1 * ((long)q));
// We validate the reminder is in a valid range.
// This is mainly to support very large numbers (like 1E+308) where the calculation could be incorrect
if (result < -Math.Abs(arg1) || result > Math.Abs(arg1))
// % result has the same sign with dividend.
// Using the following fomular to have the sign of mod result is the same as divisor as in Excel and PA Mod.
// If result has different sign with divisor, plus result with divisor to flip the sign to be the same with divisor.
if (Math.Sign(result) * Math.Sign(arg1) == -1)
{
return CommonErrors.OverflowError(irContext);
result = result + arg1;
}
return new NumberValue(irContext, result);
@ -783,25 +777,22 @@ namespace Microsoft.PowerFx.Functions
{
decimal arg0 = arg0dv.Value;
decimal arg1 = arg1dv.Value;
decimal q;
// Both decimal zero and negative zero will satisfy this test
if (arg1 == 0m)
{
return CommonErrors.DivByZeroError(irContext);
}
// r = a – N × floor(a/b)
try
{
q = decimal.Floor(arg0 / arg1);
}
catch (OverflowException)
{
return CommonErrors.OverflowError(irContext);
}
decimal result = arg0 % arg1;
decimal result = arg0 - (arg1 * q);
// % result has the same sign with dividend.
// Using the following fomular to have the sign of mod result is the same as divisor as in Excel and PA Mod.
// If result has different sign with divisor, plus result with divisor to flip the sign to be the same with divisor.
if (Math.Sign(result) * Math.Sign(arg1) == -1)
{
result = result + arg1;
}
return new DecimalValue(irContext, result);
}

Просмотреть файл

@ -410,9 +410,6 @@ Error({Kind:ErrorKind.Div0})
>> Mod(2,0)
Error({Kind:ErrorKind.Div0})
>> Mod(10^200,-10^-200)
Error({Kind:ErrorKind.Numeric})
>> Mod(Sqrt(-1),If(Char(0)="a",1))
Error(Table({Kind:ErrorKind.Numeric},{Kind:ErrorKind.InvalidArgument}))

Просмотреть файл

@ -72,7 +72,7 @@ Error({Kind:ErrorKind.Numeric})
100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
>> Mod(1E+300,1E-20)
Error({Kind:ErrorKind.Numeric})
6.0682893566359166E-21
>> Dec2Hex([1,1e-45,1e45,2])
Table({Value:"1"},{Value:"0"},{Value:Error({Kind:ErrorKind.Numeric})},{Value:"2"})

Просмотреть файл

@ -0,0 +1,19 @@
#SETUP: disable:NumberIsFloat
>> Mod(1E+27, 3)
1
>> Mod(-1E+27, -3)
-1
>> Mod(1E+19, 123)
37
>> Mod(1.2345E+26, 67.89)
0.81
>> Mod(79228162514264337593543.1234, 3.5678)
1.3776
>> Mod(79228162514264337593543950335, 123)
99

Просмотреть файл

@ -0,0 +1,35 @@
#SETUP: NumberIsFloat
>> Mod(1E+307, 3)
1
>> Mod(1E+307, -3)
-2
>> Mod(-1E+307, 3)
2
>> Mod(-1E+307, -3)
-1
>> Mod(1.7976931348623157E+300, 3)
1
>> Mod(1.2345E+260, 67.89)
41.28195726574958
>> Mod(1E+13, 123)
16
>> Mod(1E+18, 123)
16
>> Mod(1E+18, -123)
-107
// Not exact result due to floating point semantics
>> Mod(10^200,-10^-200)
-5.8180083079168396E-201
>> Mod(1E+400, 3)
Errors: Error 4-10: Numeric value is too large.|Error 0-14: The function 'Mod' has some invalid arguments.|Error 4-10: Invalid argument type (Error). Expecting a Number value instead.