I'm working on a vb.net app that needs to add multiple integers together. The integers are to be unsigned, and 32 bits in length.
It is expected that the integers will be so large that they will overflow when addition takes place. If any overflow does occur, I don't want to store any of the overflow, or there to be any exceptions that occur.
For example, If I was working with 4 bit numbers, I would want the following behaviour:
1111 + 0010 = 0001
I've tried the following to see what happens on an overflow - I get an overflow exception. Is there any elegant way around this?
Dim test As UInt32 = UInt32.MaxValue
Console.WriteLine(test.ToString())
test = test + 1
Console.WriteLine(test.ToString())
I'm currently using UInt32 to represent the integers, however this can be changed if somebody knows a better type.
It's important that the overflow bits are not stored, as later on I will want to perform bit shifts, etc on these numbers.
I can see the obvious solution of converting between UInt64 and UInt32, however I may have to expand the app in the future to use 64 bit numbers (so it would be nice to have a solution that's easily expandable)
Thanks in advance,
Dave
You can use the "Remove integer overflow checks" option in "Project" -> "<project name> Properties..." -> "Compile" tab -> "Advanced Compile Options..."
Dim a As UInt32 = UInt32.MaxValue
a += 1
Console.WriteLine(a) ' outputs 0
You will have to use 64 bit integers (they may be called Longs in your version of VB).
Dim Value1 As Long = 12345
Dim Value2 As Long = 67890
Dim Result As Long = Value1 + Value2
' Remove the overflow from the result
Console.WriteLine(Result AND &HFFFF)
You can use either signed or unsigned integers, but you will have to double your storage to make it work (e.g., 16 to 32).
Related
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.
I am doing a simple calculation in VB.Net, following is the code.
Dim total As String = "192.04"
Dim paid As String = "200"
Dim change As String = "7.96"
'//This prints -7.99360577730113E-15 (Incorrect)
MsgBox((CDbl(total) - CDbl(paid)) + CDbl(change))
The answer I expect from this calculation is 0 but I get -7.99360577730113E-15. I don't know the exact reason for this, I couldn't quite understand the reason explained on MSDN. Any clarification would be really helpful.
And I did a Math.Round to 2 decimal places & the problem was solved, therefore do I need to use Math.Round everywhere I do a calculation with decimals ?
That's because of how double (or floating point numbers in general) is represented in memory. You should use decimal for financial calculations instead of double:
double total = 192.04;
double paid = 200;
double change = 7.96;
double result = (total - paid) + change; // -7.99360577730113E-15
decimal total = 192.04m;
decimal paid = 200m;
decimal change = 7.96m;
decimal result = (total - paid) + change; // 0.00
I know it's C#, but you should see the difference anyway.
You can use Decimal.Parse to get a decimal from string:
Dim total As String = "192.04"
Dim paid As String = "200"
Dim change As String = "7.96"
MsgBox((Decimal.Parse(total) - Decimal.Parse(paid)) + Decimal.Parse(change))
This precision error is due to the way floating point numbers work in computers (more specifically the IEEE standard, see WikiPedia). In short, floating point numbers are stored as "binary numbers with decimal points" then raised to some power of 2. Therefore it cannot store exact decimal digits. This problem is common to all programming languages and not just .NET alone.
To overcome this problem, the Decimal type is used when calculating payments that require base 10 precision. Outside of .NET, almost all modern programming environments also provide this variable type.
I'm building a hex calculator in objective-c. My problem is dealing with long long values that would overflow when multiplied.
When I add values before I add i check that the value would not overflow by doing something like this.
long long leftToAdd = LLONG_MAX - self.runningTotal;
if (self.selectedNumber <= leftToAdd) {
self.runningTotal += self.selectedNumber;
} else {
self.selectedNumber -= leftToAdd;
self.runningTotal = self.selectedNumber-1;
self.overflowHasOccured = YES;
}
if the value would overflow it takes the overflow value (without actually overflowing) and adds an overflow notification. I was hoping to find a way to do this same type of thing but for multiplication, can anyone help with this?
here's what i have so far.
// if - value would not overflow //
if (self.runningTotal > 0 && self.selectedNumber > 0
&& LLONG_MAX/self.runningTotal >= self.selectedNumber) {
self.runningTotal *= self.selectedNumber;
// else - handle overflow //
} else {
}
and as a side question would i need to do a similar check for division?
You could check for overflow in multiplication following the same pattern you use for addition - for the later you use subtraction to determine the bound, for the former you would use division:
long long canMultiplyBy = LLONG_MAX / self.runningTotal;
In all cases if you are supporting signed numbers you have to consider underflow as well. Division requires a divide by zero check.
In the C library there are functions for checked arithmetic, lookup check_int64_mul to find the lot (they are all described on the same manual page). These will be efficient and operate directly with primitive value types are you are now doing.
The Clang compiler also provides some checked arithmetic builtin functions, these differ from the C library functions in returning a bool indications and being defined for int, long and long long types rather than int32 and int64. See Checked Arithmetic Builtins.
There are also NSDecimal - a value type, and NSDecimalNumber - an object type built over the former. These provide both extended precision, up to 38 decimal digits, and control over overflow, underflow, divide-by-zero, etc. See NSDecimalNumberBehaviors and NSDecimalNumberHandler.
HTH
If varShort in VB.NET is a Short and varBit is a value from 0 to 15, how can I set the bit in varShort identified by varBit without disturbing any of the other bits in varShort?
My problem, of course, is with the most significant bit, bit 15. Since varBit is determined at runtime, the solution must work with any bit number.
You can use the bitshift operators, << and >>, to turn on the bit you want (and put this value in varValue), and then bitwise Or varShort and varValue
There is information in this question about the bitshift operators in VB.NET
Setting the sixteenth bit of a Short will cause an overflow exception because Short is a signed type. Do you have any reason not to use the unsigned counterpart UShort?
Edit
If you really want to stick with Short, this function will set the sixteenth bit:
Function setNthBit(ByVal number As Short, ByVal bit As Short) As Short
Dim mask As UShort
mask = 2 ^ bit
If mask > Short.MaxValue Then
Return (Short.MinValue + number) Or mask
Else
Return number Or mask
End If
End Function
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)