I am looking to come up with a crafty and "nice-looking" way of rounding to the nearest NON-ZERO whole number (I will only ever have positive integers). Three restraints I have are:
Must be a non-UDF solution as I will have some users who will want to trace formulas. Even though a VBA solution will result in a cleaner entry in the formula bar, they are not easily traceable by "front-end" users.
Some of the cells I am looking to round are quite long formulas themselves.
Think something like =(SUMPRODUCT((A1:A20),(B1:B20)) / SUMPRODUCT((A21:A40),(B21:B40))) - (SUMPRODUCT((A41:A60),(B41:B60)) * SUMPRODUCT((A61:A80),(B61:B80))) + SUMPRODUCT((A81:A100),(B81:B100))
I am unable to round the values in A1:B100 to make it whole numbers to begin with.
I am unable to use the cells with my formulas as a reference due to how the sheet must be styled for users. (e.g. I can only have one column of visible cells with my results. Therefore, if I used the formula cells as a reference, the formula cells would have to be hidden. This breaks restraint #1 because users would have difficulty with formula tracing.) This restraint is described in more detail below.
The most succinct formula I can think of is thus:
=IF(AND(C1 > 0, C1 < 1), ROUNDUP(C1, 0), ROUND(C1, 0))
Due to #3, I cannot use C1 as a reference. So my end result would be more like:
=IF(AND((SUMPRODUCT((A1:A20),(B1:B20)) / SUMPRODUCT((A21:A40),(B21:B40))) - (SUMPRODUCT((A41:A60),(B41:B60)) * SUMPRODUCT((A61:A80),(B61:B80))) + SUMPRODUCT((A81:A100),(B81:B100)) > 0, (SUMPRODUCT((A1:A20),(B1:B20)) / SUMPRODUCT((A21:A40),(B21:B40))) - (SUMPRODUCT((A41:A60),(B41:B60)) * SUMPRODUCT((A61:A80),(B61:B80))) + SUMPRODUCT((A81:A100),(B81:B100)) < 1), ROUNDUP((SUMPRODUCT((A1:A20),(B1:B20)) / SUMPRODUCT((A21:A40),(B21:B40))) - (SUMPRODUCT((A41:A60),(B41:B60)) * SUMPRODUCT((A61:A80),(B61:B80))) + SUMPRODUCT((A81:A100),(B81:B100)), 0), ROUND((SUMPRODUCT((A1:A20),(B1:B20)) / SUMPRODUCT((A21:A40),(B21:B40))) - (SUMPRODUCT((A41:A60),(B41:B60)) * SUMPRODUCT((A61:A80),(B61:B80))) + SUMPRODUCT((A81:A100),(B81:B100)), 0))
Not very "nice-looking".
Any recommendations?
EDIT Overly complex formulas removed
Another, simpler option
=MAX(1,ROUND(your_formula,0))
My formula assumes that your_formula will only return a positive value. If that is not the case, please clarify what results you expect for zero or negative results.
EDIT
With regard to possible zero values, try this. I cannot test it since I am not at computer:
=--(TEXT(your_formula, "[>1]0;[>0]1;0"))
Except for negatives (of which there aren't any) this formula:
=(C1<>0)*IF(C1<1,1,ROUND(C1,0))
produces the same results as:
=IF(AND(C1>0,C1<1),ROUNDUP(C1,0),ROUND(C1,0))
and is 15 characters shorter.
It deals with 0 with (C1<>0) by returning FALSE when C1 is zero, which the * effectively converts to 0. Otherwise TRUE and then by multiplying by 1 instead of 0 it does not affect the result of the rest of the formula.
The IF statement then tests whether or not C1 is less than 1. If so, returns 1 and otherwise applies standard rounding (to integers).
The formula might be shortened if the output range were formatted with Cells Format but otherwise anything more succinct is quite likely to be less intelligible - and may be more a matter for Programming Puzzles & Code Golf than SO.
Though:
=--(TEXT(C1,"[>1]0;[>0]1;0"))
is indeed 4 characters shorter still.
Related
I am trying to get equal result of two exact calculations which are computed in a cell formula and the other one with a UDF:
Function calc()
Dim num as Double
num = 30000000 * ((1 + 8 / 100 / 365) ^ 125)
calc = num
End Function
Result of the calculation is different
A1 = 30000000 * ((1 + 8 / 100 / 365) ^ 125) not equal to A2 = calc()
We can test it with =if(A1=A2, TRUE, FALSE) which is false. I do understand that it has something to do with data types in vba and executing cell formula. Do you know how to make calculations to from vba function(s) and excel cell field(s) to render same result?
So, the calculation in application excel and the calculation in vba are presenting different outputs (what you've presented, with format displaying 20 decimal places):
As such, you would see false when comparing them. You will need to round() or format() to truncate the calculation at a level that is appropriate. E.g.:
calc = round(num,4)
calc = format(num,"0.###0")
The reason this is occurring is because of the inherent math you're using, specifically, ((1 + 8 / 100 / 365) ^ 125), and how that is being truncated/rounded in the allocated memory to each part of the calculation, which differs in VBA and in-application Excel.
Edit: Final image with the VBA changes I'd suggested:
Explanation
Double Data type seems to have flaws being "precise" after the "nth" digit. This is stated as well in the documentation
Precision. When you work with floating-point numbers, remember that they do not always have a precise representation in memory. This could lead to unexpected results from certain operations, such as value comparison and the Mod operator.
Troubleshooting
It seems that is the case here: I set up the value from the division on a cell and the division as formula in another one, although excel interface says there are not differences, when computing that value again, the formula on the sheet seems to be more precise.
Actual result
Further thoughts
It seems that is limited by the data type itself, if precision is not an issue, you may try to round it. If it is critical to be as precise as possible, I would suggest you to connect with an API to something that is able to handle more precision. In this scenario, I would use xlwings to use python.
Excel
Need to find nearest float in a table, for each integer 0..99
https://www.excel-easy.com/examples/closest-match.html explains a great technique for finding the CLOSEST number from an array to a constant cell.
I need to perform this for many values (specifically, find nearest to a vertical list of integers 0..99 from within a list of floats).
Array formulas don't allow the compare-to value (integers) to change as we move down the list of integers, it treats it like a constant location.
I tried Tables, referring to the integers (works) but the formula from the above web site requires an Array operation (F2, control shift Enter), which are not permitted in Tables. Correction: You can enter the formula, control-enter the array function for one cell, copy the formulas, then insert table. Don't change the search cell reference!
Update:
I can still use array operations, but I manually have to copy the desired function into each 100 target cells. No biggie.
Fixed typo in formula. See end of question for details about "perfection".
Example code:
AI4=some integer
AJ4=MATCH(MIN(ABS(Table[float_column]-AI4)), ABS(Table[float_column]-AI4), 0)
repeat for subsequent integers in AI5...AI103
Example data:
0.1 <= matches 0
0.5
0.95 <= matches 1
1.51 <= matches 2
2.89
Consider the case where target=5, and 4.5, 5.5 exist in the list. One gives -0.5 and the other +0.5. Searching for ABS(-.5) will give the first one. Either one is decent, unless your data is non-monotonic.
This still needs a better solution.
Thanks in advance!
I had another problem, which pushed to a better solution.
Specifically, since the Y values for the X that I am interested in can be at varying distances in X, I will interpolate X between the X point before and after. Ie search for less than or equal, also greater than or equal, interpolate the desired X, then interpolate the Y values.
I could go a step further and interpolate N - 1 to N + 1, which will give cleaner results for noisy data.
Consider, for example, the following function strings inside some cells:
A1 = B1 - INT(B1)
A2 = LEN(A1)
A2 will return 17 regardless of the value returned by the function (and thus held) in A1. I suspect that this has to do with the precision returned by INT(B1), but I don't know enough of Excel's inner-mechanisms to confirm.
The end goal is to obtain the length of the decimal part of a number held in B1. For example, if B1 = 978.01194, A2 would hold 5 (LEN(01194)). Obviously this would require a subtraction of 2 to eliminate the counting of the leading (0.) in my implementation above, but that's assuming I can get proper results with this method. Any help or guidance in other methods would be greatly appreciated!
EDIT:I realized that the loss of proper precision occurs only when I subtract the two quantities. INT(B1) returns proper precision, and using its length I can obtain the decimal by subtracting from the original. Would still like to know what is causing the operation in A1 to lose precision internally for LEN.
Alternatives are to use number that is not result from a calculation :
= LEN(B1) - LEN( INT(B1) ) - 1
or round the number to less than 15.95 significant digits :
= LEN( ROUND( B1 - INT(B1), 16 - LEN(INT(B1)) ) ) - 2
= LEN(TEXT(B1,"0.##############")) - LEN(INT(B1)) - 1
Another alternative is to FIND where the decimal occurs and use that as an offset, e.g.
= LEN(B1)-FIND(".",B1)
In general, it is not wise to perform a mathematical operation on a number when what you are really interested in is the text that represents the number for this exact reason. Floating points are not very reliable for dealing with exactness which is why you are experiencing the extra trailing numbers after the decimal in this case.
I'm working on a formula to get the standard deviation. It has been working not until I encountered a zero value which makes the result into #DIV/0!.
This is the screenshot of the expected value.
However, when I used my formula, the Game Time SD returned 0.
How do I exclude it in the calculation if the value in F column is zero? I tried IF(F5:F9 <> 0) but it won't work.
This is the formula I used.
F3 = IFERROR(SUBTOTAL(1,F5:F9),0)
G3 = IFERROR(SUMPRODUCT(SUBTOTAL(2,OFFSET(F5:F9,ROW(F5:F9)-MIN(ROW(F5:F9)),,1))*(G5:G9*F5:F9))/SUBTOTAL(9,F5:F9),0)
H3 = IFERROR(((SUBTOTAL(9,F5:F9)*(SUMPRODUCT(SUBTOTAL(2,OFFSET(F5:F9,ROW(F5:F9)-MIN(ROW(F5:F9)),,1)) *
((H5:H9^2*F5:F9*(F5:F9-1)+(G5:G9*F5:F9)^2)/F5:F9)))-(SUMPRODUCT(SUBTOTAL(9,OFFSET(G5:G9,ROW(G5:G9)-MIN(ROW(G5:G9)),,1)),SUBTOTAL(9,OFFSET(F5:F9,ROW(F5:F9)-MIN(ROW(F5:F9)),,1))))^2)/(SUBTOTAL(9,F5:F9)*(SUBTOTAL(9,F5:F9)-1)))^(1/2),0)
I know the problem is somewhere in F5:F9, since the divisor used is zero
The part you suspected in the code involves dividing by a denominator that happens to be a factor in the numerator. You can avoid a division by zero by simplifying that fraction.
((H5:H9^2*F5:F9*(F5:F9-1)+(G5:G9*F5:F9)^2)/F5:F9)))
can be reduced to
(H5:H9^2*(F5:F9-1) + (G5:G9^2*F5:F9))
Resulting in the formula (3rd line modified)
=IFERROR(((SUBTOTAL(9,F5:F9)*
(SUMPRODUCT(SUBTOTAL(2,OFFSET(F5:F9,ROW(F5:F9)-MIN(ROW(F5:F9)),,1))*
(H5:H9^2*(F5:F9-1) + (G5:G9^2*F5:F9))))-
(SUMPRODUCT(SUBTOTAL(9,OFFSET(G5:G9,ROW(G5:G9)-
MIN(ROW(G5:G9)),,1)),SUBTOTAL(9,OFFSET(F5:F9,ROW(F5:F9)-
MIN(ROW(F5:F9)),,1))))^2)/(SUBTOTAL(9,F5:F9)*
(SUBTOTAL(9,F5:F9)-1)))^(1/2), 0)
In my tests without the enclosing IFERROR, I could set some rows to zero and get values. Only when the square rooted subtotal was negative (which logically should not happen) was the result #NUM.
Hope this helps.
I am adding a constraint to Excel Solver.
The constraint is saying an input data at location A3 must be either 0 or between 3 and 8.
How could I add this constraint in Excel Solver since I could not find 'or' function in solver constraint box. It only has ">=", "<=", "=", "int", "bin", "dif" operators there.
Many Thanks
This is a little bit of a hack, but you could create a function that is zero when your constraints are met, and nonzero when they are not. For example, if you have (in named ranges) a number of individual values val_1, val_2 etc. that are all valid, and a range llim and ulim (lower and upper limit), then the following equation will evaluate to zero if your conditions are met:
=(A3 - val_1) * (A3 - val_2) * FLOOR.PRECISE(ABS((A3 - (llim + ulim) / 2) / ((ulim - llim) / 2)))
When cell A3 is either val_1 or val_2, you will multiply your expression by zero; and when A3 is between llim and ulim, the expression inside the FLOOR.PRECISE() function will evaluate to something smaller than 1 - so the FLOOR will be zero.
Enter that expression in a cell, and make your constraint that this cell must be zero... It ought to work. It did for me - with the function to optimize being "3 * A3", and limits set to 3 and 8, the solution was 7.99999.
Note - one problem with this is that you will most likely get the solver "stuck" in just one interval - it will never notice the other possible values. If that is important, you may have to come up with a transformation of a continuous variable into one that has discrete values plus a range. Example:
=IF(A3<-1,val_1,IF(A3<0,val_2,llim+(ulim-llim)*ATAN(A3)*2/PI()))
Now A3 can vary over the full range, and yet the cell with this formula in it will always have a "valid" value in it. Again, if you need more fixed values you can add more nested IF statements...
It is used for the purpose where the situation is if one condition is also correct then it should show true.