Why is Excel VBA rounding my integer division results? - excel

I'm writing a VBA macro in Excel 2002 to manipulate date from a device that gives 10 values on each of 4 channels.
When I try to divide an integer by another integer to calculate the row and column, the result shows that it's using bankers rounding, as if it was a floating point variable.
Sub TestRounding()
Dim Y As Integer
Dim Field As Integer
Dim Channel As Integer
ActiveWorkbook.Sheets.Add
ActiveWorkbook.ActiveSheet.Cells.Select
range("A1").Select
For Field = 1 To 40
Channel = (Field - 1) / 10
Y = Field - 1
ActiveSheet.range("A1").Offset(Y, Channel).Value = Field
Next Field
End Sub
Y is the row to put the output in, which I usually set with
Y = (Field -1) mod 10
but I've left it as
Y = Field - 1
to illustrate the point more clearly in the resulting worksheet.
Channel is supposed to be the column that the output is put in.
When I run this macro, the values 1-6 get put in column A, then 7-15 get put into Column B, 16-26 get put into Column C, then 27-35 in Column D then 36-40 in Column E.
What I expected was that values 1-10 go into column A, 11-20 into column B, 21-30 into column C and 31-40 into column D.
I'm used to C and C++ where if I divide an integer by another integer, the result is calculated using integer maths. What's different in VBA?

To use "integer maths" for division use \ instead of /.
Integer Division \
Dividing an item means cutting it in pieces or fractions of a set
value. Therefore, the division is used to get the fraction of one
number in terms of another. The Visual Basic language provides two
types of operations for the division. If you want the result of the
operation to be a natural number, called an integer, use the backlash
operator "\" as the divisor. The formula to use is: Value1 \ Value2
This operation can be performed on two types of valid numbers, with or
without decimal parts. After the operation, the result would be a
natural number.
Decimal Division /
The second type of division results in a decimal
number. It is performed with the forward slash "/". Its formula is:
Value1 / Value2 After the operation is performed, the result is a
decimal number.
Source

The back slash is used for integer division, and the forward slash for decimal division. Change your forward slash to a back slash and you should get the result you are expecting.
http://www.functionx.com/vbaexcel/Lesson04.htm

Related

VBA function for Upside/Downside Capture

apologies for my ignorance, I'm brand new to VBA - I'm sure this is a simple problem...
I'm trying to write a fn. for up/down side capture in VBA. This is the problem:
There are two columns. One has fund performance in % (I've labelled 'returns'). The other has index performance in % (labelled 'index'). Both are same length / same number of rows. I need both to be variables to enter to the fn.
For UpsideCapture fn., for all nos. in the index column >0, I want to find the corresponding number in the returns column (which will be on the same row). Once I have those numbers I can compound them.
I've tried using Offset, assuming the returns column is 15 columns to the left of the index column but it doesn't return anything, and I don't really want to rely on it always being 15 columns apart (it arbitrary).
Many thanks!
One of my rubbish attempts is below. Any help is much appreciated. Its really just a case of finding the correct corresponding row based on the value in the index column...
Function UpsideCapture(returns As Range, index As Range) As Variant
Dim n As Integer
Dim m As Integer
Dim i As Integer
n = returns.Rows.Count
m = index.Rows.Count
For i = 1 To m
If index(i) > 0 Then
Upsidecap = ((1 + Upsidecap) * (1 + Offset(returns(i), -15))) - 1
End If
Next
UpsideCapture = Upsidecap
End Function
example

Excel VBA Function Fails but Formula works in sheet

Weird Math Error in VBA for Excel
Hi all, would love feedback on unsusual error I'm getting.
Very strange. I have a simple formula that works great if I
only use it in a normal sheet cell and copy it down by columns,
but if I try to do a simple iteration in vba code to perform the same function I get the wrong values.
Description : A number is squared, then divided by another value
between 0.99 to 1.99, next the modulus is taken and then
the number is squared again and the whole formula repeated.
If I copy the formula statement down column wise it calcs fine,
including reasonable decimal accuracy.
There are four inputs ;
base value (inputx)
decx = divisor
mod value
The first formula placed at (E2) looks like ; =MOD(((B2^2)/$B$3),$B$4)
In (E3) this statement is placed ; =MOD(((E2^2)/$B$3),$B$4)
Then this exact same statement is copied down, columnwise to the next 98 cells.
All great, no problem. It seems accurate value wise, right to decimal
precision, with values past the decimal point showing in all column cells.
Some sample input values for testing ;
INPUTX --> 231
DECX 1.010101
MOD 400
LOOPTIMES 100
But when I try to implement this is Excel VBA code (Excel 2007)
I often get the wrong values and absolutely no values past the decimal point ever show.
Have tried using all kinds of different data types ; single, double, variant, etc... but all values returned by the VBA function I made always returns
whole numbers, and is often wrong and certainly does not agree with the
values returned by the simple column based statements.
Have tried to find ways around this or to fix this, came across "CDEC", tried
this and nothing changed. Totally stumped and would love some insight into
if this can be fixed so that the function loop returns the same values with
same kind of decimal precision as the column based statements and
would greatly appreciate feedback on how mthis can be done.
Am including my sample code below ;
Public Function SQRD(inputx As Variant, looptime As Variant, decx As Variant) As Variant
Application.Volatile
Dim Count As Integer
SQRD = CDec(inputx)
'Dim decx As variant
Count = 1
For Count = 1 To looptime
SQRD = CDec(SQRD ^ 2) '+ looptime
SQRD = CDec(SQRD Mod 400 / decx)
Next Count
End Function
I will only address your use of the VBA Mod operator. It is NOT equivalent to the Excel MOD function. In particular, the VBA Mod operator will round floating point numbers to integers before performing the operation.
To use a VBA function that mimics the Excel MOD function, use something like:
Function xlMOD(a As Double, b As Double) As Double
xlMOD = a - (b * (a \ b))
End Function
EDIT
There seems to be a bug in VBA (or a documentation error). In the formula above, the \ operator is supposed to be the integer division operator. It does return an integer result. However, it does not truncate, rather it rounds. Actually, what it may be doing, is performing VBA type rounding on the number and divisor, before returning the result.
Therefore, a proper vba function to mimic the Excel MOD function would be:
Function xlMOD(a As Double, b As Double) As Double
xlMOD = a - Int(a / b) * b
End Function
Lots amiss with your code. No need for looping as far as I can see, and you're dividing after the mod not before
This seems to do the trick
Public Function NuFunc(InputX As Variant, DecX As Variant) As Variant
NuFunc = ((InputX ^ 2) / DecX) Mod 400
End Function

Display Value Does Not Match Data Value After Split Function

I'm writing a macro that parses a string in a cell from my excel sheet and should return the three coordinates in that string. I can get the script to parse the string fine and create a "coordinateHolder" array to hold the three coordinates. My issue is that when I update cells to show the coordinates excel shows does not show the entire coordinate.
For example, if the coordinates string is originally "1234.1324123, 12345.23521, 2384.1234253", my code will update my x, y, and z coordinate cells as "1234.132", "12345", "2384.1234"
Image of what I mean:
(This one shows scientific notation in the cell and a shortened double in the formula builder bar)
My Code:
Dim i, j As Integer
Dim coordinates As String
Dim coordHolder As Variant
i = 2
j = 1
Range("I2:K2").Value = Range("E2:G2").Value
Do While Cells(i, j) <> ""
coordinates = Cells(i, j)
coordinates = Replace(coordinates, ",", "")
coordHolder = Split(coordinates, " ")
For a = 0 To UBound(coordHolder)
Cells(i, 7 + a) = coordHolder(a)
Next a
i = i + 1
Loop
Excel has a limit of 15 digits for numbers. Any number with more digits will lose precision in the lower magnitudes to enable the number display. Your data has values that go beyond the limit and will be truncated.
513402938412.123 shows just 15 digits. The remaining decimal places have been removed. The significance of 4 or more decimals pales in comparison with the magnitude of the value, therefore Excel considers the digits after the third deicmal as dispensable.
If you want to retain all digits in the value, you need to convert it to text and make sure it remains text and is not converted to a number again. To do that, you can precede any number with the apostrophe sign.
If a cell contains the value 5134029388412.12341234 it will be truncated. A cell containing the value '5134029388412.12341234 will be treated as text and remain intact.

How to get excel to display a certain number of significant figures?

I am using excel and i want to display a value to a certain number of significant figures.
I tried using the following equation
=ROUND(value,sigfigs-1-INT(LOG10(ABS(value))))
with value replaced by the number I am using and sigfigs replaced with the number of significant figures I want.
This formula works sometimes, but other times it doesn't.
For instance, the value 18.036, will change to 18, which has 2 significant figures. The way around this is to change the source formatting to retain 1 decimal place. But that can introduce an extra significant figure. For instance, if the result was 182 and then the decimal place made it change to 182.0, now I would have 4 sig figs instead of 3.
How do I get excel to set the number of sig figs for me so I don't have to figure it out manually?
The formula (A2 contains the value and B2 sigfigs)
=ROUND(A2/10^(INT(LOG10(A2))+1),B2)*10^(INT(LOG10(A2))+1)
may give you the number you want, say, in C2. But if the last digit is zero, then it will not be shown with a General format. You have then to apply a number format specific for that combination (value,sigfigs), and that is via VBA. The following should work. You have to pass three parameters (val,sigd,trg), trg is the target cell to format, where you already have the number you want.
Sub fmt(val As Range, sigd As Range, trg As Range)
Dim fmtstr As String, fmtstrfrac As String
Dim nint As Integer, nfrac As Integer
nint = Int(Log(val) / Log(10)) + 1
nfrac = sigd - nint
If (sigd - nint) > 0 Then
'fmtstrfrac = "." & WorksheetFunction.Rept("0", nfrac)
fmtstrfrac = "." & String(nfrac, "0")
Else
fmtstrfrac = ""
End If
'fmtstr = WorksheetFunction.Rept("0", nint) & fmtstrfrac
fmtstr = String(nint, "0") & fmtstrfrac
trg.NumberFormat = fmtstr
End Sub
If you don't mind having a string instead of a number, then you can get the format string (in, say, D2) as
=REPT("0",INT(LOG10(A2))+1)&IF(B2-(INT(LOG10(A2))+1)>0,"."&REPT("0",B2-(INT(LOG10(A2))+1)),"")
(this replicates the VBA code) and then use (in, say, E2)
=TEXT(C2,D2).
where cell C2 still has the formula above. You may use cell E2 for visualization purposes, and the number obtained in C2 for other math, if needed.
WARNING: crazy-long excel formula ahead
I was also looking to work with significant figures and I was unable to use VBA as the spreadsheets can't support them. I went to this question/answer and many other sites but all the answers don't seem to deal with all numbers all the time. I was interested in the accepted answer and it got close but as soon as my numbers were < 0.1 I got a #value! error. I'm sure I could have fixed it but I was already down a path and just pressed on.
Problem:
I needed to report a variable number of significant figures in positive and negative mode with numbers from 10^-5 to 10^5. Also, according to the client (and to purple math), if a value of 100 was supplied and was accurate to +/- 1 and we wish to present with 3 sig figs the answer should be '100.' so I included that as well.
Solution:
My solution is for an excel formula that returns the text value with required significant figures for positive and negative numbers.
It's long, but appears to generate the correct results according to my testing (outlined below) regardless of number and significant figures requested. I'm sure it can be simplified but that isn't currently in scope. If anyone wants to suggest a simplification, please leave me a comment!
=TEXT(IF(A1<0,"-","")&LEFT(TEXT(ABS(A1),"0."&REPT("0",sigfigs-1)&"E+00"),sigfigs+1)*10^FLOOR(LOG10(TEXT(ABS(A1),"0."&REPT("0",sigfigs-1)&"E+00")),1),(""&(IF(OR(AND(FLOOR(LOG10(TEXT(ABS(A1),"0."&REPT("0",sigfigs-1)&"E+00")),1)+1=sigfigs,RIGHT(LEFT(TEXT(ABS(A1),"0."&REPT("0",sigfigs-1)&"E+00"),sigfigs+1)*10^FLOOR(LOG10(TEXT(ABS(A1),"0."&REPT("0",sigfigs-1)&"E+00")),1),1)="0"),LOG10(TEXT(ABS(A1),"0."&REPT("0",sigfigs-1)&"E+00"))<=sigfigs-1),"0.","#")&REPT("0",IF(sigfigs-1-(FLOOR(LOG10(TEXT(ABS(A1),"0."&REPT("0",sigfigs-1)&"E+00")),1))>0,sigfigs-1-(FLOOR(LOG10(TEXT(ABS(A1),"0."&REPT("0",sigfigs-1)&"E+00")),1)),0)))))
Note: I have a named range called "sigfigs" and my numbers start in cell A1
Test Results:
I've tested it against the wikipedia list of examples and my own examples so far in positive and negative. I've also tested with a few values that gave me issues early on and all seem to produce the correct results.
I've also tested with a few values that gave me issues early on and all seem to produce the correct results now.
3 Sig Figs Test
99.99 -> 100.
99.9 -> 99.9
100 -> 100.
101 -> 101
Notes:
Treating Negative Numbers
To Treat Negative Numbers, I have included a concatenation with a negative sign if less than 0 and use the absolute value for all other work.
Method of construction:
It was initially divided into about 6 columns in excel that performed the various steps and at the end I merged all of the steps into one formula above.
Use scientific notation, say if you have 180000 and you need 4 sigfigs the only way is to type as 1.800x10^5
I added to your formula so it also automatically displays the correct number of decimal places. In the formula below, replace the digit "2" with the number of decimal places that you want, which means you would need to make four replacements. Here is the updated formula:
=TEXT(ROUND(A1,2-1-INT(LOG10(ABS(A1)))),"0"&IF(INT(LOG10(ABS(ROUND(A1,2-1-INT(LOG10(ABS(A1)))))))<1,"."&REPT("0",2-1-INT(LOG10(ABS(ROUND(A1,2-1-INT(LOG10(ABS(A1)))))))),""))
For example, if cell A1 had the value =1/3000, which is 0.000333333.., the above formula as-written outputs 0.00033.
This is an old question, but I've modified sancho.s' VBA code so that it's a function that takes two arguments: 1) the number you want to display with appropriate sig figs (val), and 2) the number of sig figs (sigd). You can save this as an add-in function in excel for use as a normal function:
Public Function sigFig(val As Range, sigd As Range)
Dim nint As Integer
Dim nfrac As Integer
Dim raisedPower As Double
Dim roundVal As Double
Dim fmtstr As String
Dim fmtstrfrac As String
nint = Int(Log(val) / Log(10)) + 1
nfrac = sigd - nint
raisedPower = 10 ^ (nint)
roundVal = Round(val / raisedPower, sigd) * raisedPower
If (sigd - nint) > 0 Then
fmtstrfrac = "." & String(nfrac, "0")
Else
fmtstrfrac = ""
End If
If nint <= 0 Then
fmtstr = String(1, "0") & fmtstrfrac
Else
fmtstr = String(nint, "0") & fmtstrfrac
End If
sigFig = Format(roundVal, fmtstr)
End Function
It seems to work in all the use cases I've tried so far.
Rounding to significant digits is one thing... addressed above. Formatting to a specific number of digits is another... and I'll post it here for those of you trying to do what I was and ended up here (as I will likely do again in the future)...
Example to display four digits:
.
Use Home > Styles > Conditional Formatting
New Rule > Format only cells that contain
Cell Value > between > -10 > 10 > Format Number 3 decimal places
New Rule > Format only cells that contain
Cell Value > between > -100 > 100 > Format Number 2 decimal places
New Rule > Format only cells that contain
Cell Value > between > -1000 > 1000 > Format Number 1 decimal place
New Rule > Format only cells that contain
Cell Value > not between > -1000 > 1000 > Format Number 0 decimal places
.
Be sure these are in this order and check all of the "Stop If True" boxes.
The formula below works fine. The number of significant figures is set in the first text formula. 0.00 and 4 for 3sf, 0.0 and 3 for 2sf, 0.0000 and 6 for 5sf, etc.
=(LEFT((TEXT(A1,"0.00E+000")),4))*POWER(10,
(RIGHT((TEXT(A1,"0.00E+000")),4)))
The formula is valid for E+/-999, if you have a number beyond this increase the number of the last three zeros, and change the second 4 to the number of zeros +1.
Note that the values displayed are rounded to the significant figures, and should by used for display/output only. If you are doing further calcs, use the original value in A1 to avoid propagating minor errors.
As a very simple display measure, without having to use the rounding function, you can simply change the format of the number and remove 3 significant figures by adding a decimal point after the number.
I.e. #,###. would show the numbers in thousands. #,###.. shows the numbers in millions.
Hope this helps
You could try custom formatting instead.
Here's a crash course: https://support.office.com/en-nz/article/Create-a-custom-number-format-78f2a361-936b-4c03-8772-09fab54be7f4?ui=en-US&rs=en-NZ&ad=NZ.
For three significant figures, I type this in the custom type box:
[>100]##.0;[<=100]#,##0
You could try
=ROUND(value,sigfigs-(1+INT(LOG10(ABS(value)))))
value :: The number you wish to round.
sigfigs :: The number of significant figures you want to round to.

Is Excel's FALSE like infinity?

Why does =FALSE<10000000000 evaluate as FALSE and =FALSE>10000000000 evaluate as TRUE? I have tried some different numbers and this seems to always be the case.
This is by design. Search help for "Troubleshoot Sort" to see the default sort order.
In an ascending sort, Microsoft Excel uses the following order.
Numbers: Numbers are sorted from the smallest negative number to the largest positive number.
Alphanumeric sort: When you sort alphanumeric text, Excel sorts left to right, character by character. For example, if a cell contains the text "A100," Excel places the cell after a cell that contains the entry "A1" and before a cell that contains the entry "A11."
Text and text that includes numbers are sorted in the following order:
0 1 2 3 4 5 6 7 8 9 (space) ! " # $ % & ( ) * , . / : ; ? # [ \ ] ^ _ ` { | } ~ + < = > A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
Apostrophes (') and hyphens (-) are ignored, with one exception: If two text strings are the same except for a hyphen, the text with the hyphen is sorted last.
Logical values: In logical values, FALSE is placed before TRUE.
Error values: All error values are equal.
Blanks: Blanks are always placed last.
The default sort order matters because that is how Excel was designed to compare different data types. Logical values are always after text and numbers. Error values are always after that. Blanks are always last. When you use comparison operators (<, <=, =, etc.) it uses the same comparison algorithm as the sort (or more likely, the sort alogrithm uses the comparison operator code, which makes them identical).
TRUE<>1 according to the sort order, but --TRUE=1. The formula parser recognized that you're trying to negate something. If it's a Boolean value, it converts it to 0 or 1. There's nothing 0-ish or 1-ish about the Boolean value, it's just the result of an internal Type Coercion function. If you type --"SomeString" it does the same thing. It sends the string into the Type Coercion function that reports back 'Unable to coerce' and ends up as #VALUE! in the cell.
That's the 'Why it behaves that way' answer. I don't know the 'Why did they design it that way' answer.
Obviously the boolean TRUE/FALSE are different data types to numbers. Check this (http://msdn.microsoft.com/en-us/library/office/bb687869.aspx) to see that boolean variables are stored in 2-byte (or whatever a short integer is for a certain architecture). However this is the memory where the data is stored, because excel actually has a special data class for boolean vars. Specifically: xltypeNum for numbers, xltypeStr for strings, and xltypeBool for what we discuss.
The relations between same types is clear, now what TRUE<1000 does?? probably nothing meaningful-useful.
Ways to overcome this issue:
=ABS(BOOLEAN_VAR), i.e. =ABS(FALSE) --> 0 and =ABS(TRUE) --> 1
or
=INT(BOOLEAN_VAR), i.e. =INT(FALSE) --> 0 and =INT(TRUE) --> 1
or
=BOOLEAN_VAR*1, i.e. =FALSE*1 --> 0 and =TRUE*1 --> 1
or
=+BOOLEAN_VAR, i.e. =+FALSE --> 0 and =+TRUE --> 1
As you see in these ways you force excel to output a numeric type of data, either by providing the boolean into a function or using the boolean var in an expression.

Resources