Mod with Float Numbers gives wrong Result [duplicate] - vba

This question already has answers here:
VBA equivalent to Excel's mod function
(9 answers)
Closed 5 years ago.
When I try this Code:
A = 19 Mod 6.7
the Result is 5 but it should be 5.6
Can someone say me, whats going wrong?

Annoyingly, VBA will round the 6.7 to 7 as it doesn't have a floating point modulus function (cf. Java).
Effectively it evaluates 19 Mod CInt(6.7), and CInt uses German Rounding, rather than integer truncation.
If you want to duplicate the behaviour of the MOD function on a worksheet, see VBA equivalent to Excel's mod function

MOD is an integer function in VBA. It would be better to use:
a - (b * (a / b))

Related

VBA rounding issue (that's not banker's rounding) [duplicate]

This question already has answers here:
VBA Double vs Single rounding
(1 answer)
Why does Bankers Rounding function in Excel VBA give different results depending on number of decimal places?
(1 answer)
Closed last month.
I'm having an issue with the Round function in VBA. I'm aware that it uses Banker's Rounding but the results still seem inconsistent to me:
? Round(1.425, 2)
1.42
? Round(2.425, 2)
2.42
? Round(3.425, 2)
3.42
? Round(4.425, 2)
4.42
? Round(5.425, 2)
5.42
? Round(6.425, 2)
6.42
? Round(7.425, 2)
7.42
? Round(8.425, 2)
8.43
? Round(9.425, 2)
9.43
? Round(10.425, 2)
10.42
? Round(11.425, 2)
11.42
? Round(12.425, 2)
12.42
If I understand Banker's Rounding, all of these should round to x.42 but 8.425 and 9.425 don't. This is in Excel 16.66 for Mac but I've verified it in other products as well.
EDIT
I confirmed the same behaviour occurs in C# as well when I use double data types. But when I use decimal, all is right again. Which leads me to believe this is an issue with floating points in VBA.

What does % mean in this statement in kotlin? [duplicate]

This question already has answers here:
Understanding The Modulus Operator %
(10 answers)
Closed 2 years ago.
I am learning kotlin and in a snipped of code iterating through a list i have this line:
currentIndex = (currentIndex + 1) % myList.size
I am confused by the % sign. Does it mean that you should increment currentIndex by 1 until you get to the size of myList?
I couldnt find anything in documentation about it and the text book just seems to assume I should know!
Modulus operator. Take a look to the next links:
https://kotlinlang.org/docs/reference/operator-overloading.html
https://www.programiz.com/kotlin-programming/operators
EDIT 1: Sorry, I forgot to answer your question really.
What it makes that statement is to increment the currentIndex by 1 at each iteration, until it ranges the whole list. When that happens, the currentIndex is initialized to 0 again. When currentIndex = 9, the next iteration will be set to zero again.
Cheers.

VBA Fix Function Discrepancy? [duplicate]

Disclaimer: I know that 0.025 cannot be represented exactly in IEEE floating-point variables and, thus, rounding might not return what one would expect. That is not my question!
Is it possible to simulate the behavior of the VBA arithmetic operators in .NET?
For example, in VBA, the following expression yields 3:
Dim myInt32 As Long
myInt32 = CLng(0.025 * 100) ' yields 3
However, in VB.NET, the following expression yields 2:
Dim myInt32 As Integer
myInt32 = CInt(0.025 * 100) ' yields 2
According to the specification, both should return the same value:
Long (VBA) and Integer (VB.NET) are 32-bit integer types.
According to the VBA specification, CLng performs Let-coercion to Long, and Let-coercion between numeric types uses Banker's rounding. The same is true for VB.NET's CInt.
0.025 is a double precision IEEE floating-point constant in both cases.
Thus, some implementation detail of the floating-point multiplication operator or the integer-conversion operator changed. However, for reasons of compatibility with a legacy VBA system, I'd need to replicate the mathematical behavior of VBA (however wrong it might be) in a .NET application.
Is there some way to do that? Did someone write a Microsoft.VBA.Math library? Or is the precise VBA algorithm documented somewhere so I can do that myself?
VBA and VB.NET behave differently because VBA uses 80-bit "extended" precision for intermediate floating-point calculations (even though Double is a 64-bit type), whereas VB.NET always uses 64-bit precision. When using 80-bit precision, the value of 0.025 * 100 is slightly greater than 2.5, so CLng(0.025 * 100) rounds up to 3.
Unfortunately, VB.NET doesn't seem to offer 80-bit precision arithmetic. As a workaround, you can create a native Win32 DLL using Visual C++ and call it via P/Invoke. For example:
#include <cmath>
#include <float.h>
#pragma comment(linker, "/EXPORT:MultiplyAndRound=_MultiplyAndRound#16")
extern "C" __int64 __stdcall MultiplyAndRound(double x, double y)
{
unsigned int cw = _controlfp(0, 0);
_controlfp(_PC_64, _MCW_PC); // use 80-bit precision (64-bit significand)
double result = floor(x * y + 0.5);
if (result - (x * y + 0.5) == 0 && fmod(result, 2))
result -= 1.0; // round down to even if halfway between even and odd
_controlfp(cw, _MCW_PC); // restore original precision
return (__int64)result;
}
And in VB.NET:
Declare Function MultiplyAndRound Lib "FPLib.dll" (ByVal x As Double, ByVal y As Double) As Long
Console.WriteLine(MultiplyAndRound(2.5, 1)) ' 2
Console.WriteLine(MultiplyAndRound(0.25, 10)) ' 2
Console.WriteLine(MultiplyAndRound(0.025, 100)) ' 3
Console.WriteLine(MultiplyAndRound(0.0025, 1000)) ' 3
Given that the VBA is supposed to use Banker's rounding, it seems clear to me at first glance that the bug is actually in the VBA side of things. Bankers rounding rounds at the midpoint (.5) so the result digit is even. Thus, to do correct Banker's rounding, 2.5 should round to 2, and not to 3. This matches the .Net result, rather than the VBA result.
However, based on information pulled from a currently deleted answer, we can also see this result in VBA:
Dim myInt32 As Integer
myInt32 = CInt(2.5) ' 2
myInt32 = CInt(0.025 * 100) ' 3
This makes it seem like the rounding in VBA is correct, but the multiplication operation produces a result that is somehow greater than 2.5. Since we're no longer at a mid-point, the Banker's rule does not apply, and we round up to 3.
Therefore, to fix this issue, you'll need to figure out what that VBA code is really doing with that multiplication instruction. Regardless of what is documented, the observations prove that VBA is handling this part differently than .Net. Once you figure out exactly what's going on, with luck you'll be able to simulate that behavior.
One possible option is to go back to the old standby for floating point numbers: check whether you're within some small delta of a mid-point and, if so, just use the mid-point. Here's some (untested) naive code to do it:
Dim result As Double = 0.025 * 100
Dim delta As Double = Double.Epsilon
Dim floor As Integer = Math.Floor(result)
If Math.Abs(result - (CDbl(floor) + 0.5)) <= delta Then
result = floor + 0.5
End
I emphasize the untested, because at this point we're already dealing strange with results from small computer rounding errors. The naive implementation in this situation is unlikely to be good enough. At very least, you may want to use a factor of 3 or 4 epsilons for your delta. Also, the best you could hope for from this code is that it could force the VBA to match the .Net, when what you're really after is the reverse.

Determing longest repeating cycle in a decimal expansion

Today I encountered this article about decimal expansion and I was instantaneously inspired to rework my solution on Project Euler Problem 26 to include this new knowledge of math for a more effecient solution (no brute forcing). In short the problem is to find the value of d ranging 1-1000 that would maximize the length of the repeating cycle in the expression "1/d".
Without making any further assumptions about the problem that could further improve the effecienty of solving the problem I decided to stick with
10^s=10^(s+t) (mod n)
which allows me for any value of D to find the longest repeating cycle (t) and the starting point for the cycle (s).
The problem is that eksponential part of the equation, since this will generate extremely large values before they're reduced by using modulus. No integral value can handle this large values, and the floating point data types seemes to be calculating wrong.
I'm using this code currently:
Private Function solveDiscreteLogarithm(ByVal D As Integer) As Integer
Dim NumberToIndex As New Dictionary(Of Long, Long)()
Dim maxCheck As Integer = 1000
For index As Integer = 1 To maxCheck
If (Not NumberToIndex.ContainsKey((10 ^ index) Mod D)) Then
NumberToIndex.Add((10 ^ index) Mod D, index)
Else
Return index - NumberToIndex((10 ^ index) Mod D)
End If
Next
Return -1
End Function
which at some point will compute "(10^47) mod 983" resulting in 783 which is not the correct result. The correct result should have been 732. I'm assuming it's because I'm using integral data types and it's causing overflow. I tried using double instead, but that gave even stranger results.
So what are my options?
Instead of using ^ to do your powers, I would do a for loop using multiplication and then taking the mod of the number as you go along by using a conditional to check if the number calculated is greater than the mod. This helps to keep the numbers smaller and within range of your mod number.
I'll give you a hint from my own solution to this.
With each decimal expansion of the fraction, you end up with a remainder, which if multiplied by the current decimal place, is an integer. Since this remainder is all you need to determine the next decimal expansion, you can use it to make predictions about the subsequent expansion.
See my post for this other question, getting the nth digit of a fraction, you may find some useful leads on what to try. (Methinks the answer is the largest prime less than 1000.) (Correction: the largest prime or Carmichael number less than 1000.)

Misunderstanding of Long data type in VBA

From the help for the Overflow Error in VBA, there's the following examples:
Dim x As Long
x = 2000 * 365 ' gives an error
Dim x As Long
x = CLng(2000) * 365 ' fine
I would have thought that, since the Long data type is supposed to be able to hold 32-bit numbers, that the first example would work fine.
I ask this because I have some code like this:
Dim Price as Long
Price = CLng(AnnualCost * Months / 12)
and this throws an Overflow Error when AnnualCost is 5000 and Months is 12.
What am I missing?
2000 and 365 are Integer values. In VBA, Integers are 16-bit signed types, when you perform arithmetic on 2 integers the arithmetic is carried out in 16-bits. Since the result of multiplying these two numbers exceeds the value that can be represented with 16 bits you get an exception. The second example works because the first number is first converted to a 32-bit type and the arithmetic is then carried out using 32-bit numbers. In your example, the arithmetic is being performed with 16-bit integers and the result is then being converted to long but at that point it is too late, the overflow has already occurred. The solution is to convert one of the operands in the multiplication to long first:
Dim Price as Long
Price = CLng(AnnualCost) * Months / 12
The problem is that the multiplication is happening inside the brackets, before the type conversion. That's why you need to convert at least one of the variables to Long first, before multiplying them.
Presumably you defined the variables as Integer. You might consider using Long instead of Integer, partly because you will have fewer overflow problems, but also because Longs calculate (a little) faster than Integers on 32 bit machines. Longs do take more memory, but in most cases this is not a problem.
In VBA, literals are integer by default (as mentioned). If you need to force a larger datatype on them you can recast them as in the example above or just append a type declaration character. (The list is here: http://support.microsoft.com/kb/191713) The type for Long is "&" so you could just do:
Price = CLng(AnnualCost * Months / 12&)
And the 12 would be recast as a long. However it is generally good practice to avoid literals and use constants. In which case you can type the constant in it's declaration.
Const lngMonths12_c as Long = 12
Price = CLng(AnnualCost * Months / lngMonths12_c)