Let's say you have a formula that produced the following numbers:
50.77%
24.62%
24.62%
Added together they equal 100%, however they do not want the decimal places shown, and the number still visually showing they equal 100%. If you take the decimal places out you get 51% 25% 25% which actually equal 101%.
They suggested that I put some type of formula that will round the largest decimal place up.. ie... 50.77% becomes 51%, and round the others down... if it goes over 100%, if it is still not equal to 100%, round up the next largest number until and so on until the total does equal 100%.
Anyone know what type of formula of vba coding that I could use for this?
I do not even know where to start.
You can use helper columns with this formula:
=IF(SUM($B$1:B1)+ROUND(A2,2)>1,1-SUM($B$1:B1),ROUND(A2,2))
Unless I'm missing something, you can do: =TEXT(SUM(A1:A3),"##"), assuming your numbers are in A1:A3.
What I usually do in cases like this is to set a formula at the last number (e.g. A4) like this one :
=1-(SUM(A1:A3))
Edit:
This is what I do, if I have to use vba:
Calculate the whole range. Round each number as you wish.
See the difference which you should add or substract (e.g. if you get the sum
to 99.9%, then your difference is 0.01).
Add this difference to the last number in the range. Via VBA.
Even if it is negative, (e.g. if you have a sum of 100.01) then you should sum with a negative number, thus anything is ok.
The only I can think of is "cheating" in a really nasty way... assuming that you have formulas, which do the calculation, a replacement is not possible... So I simply change the formatting (and keep the old values) like this:
Private Sub recalc_percs()
Dim RangeToCheck As Range
Set RangeToCheck = Range("A5:A9") 'designet to work with a range containing only 1 area and 1 column
Dim x(1) As Variant, i As Long, b(2) As Double
RangeToCheck.NumberFormat = "0%"
x(0) = Evaluate(RangeToCheck.Address & "*100")
x(1) = Evaluate("INDEX(ROUND(" & RangeToCheck.Address & "*100,0),)")
While Application.Sum(x(1)) <> 100 And Abs(100 - Application.Sum(x(1))) < 4 'the <4 is for deleting or whatever to not loop endless times
For i = 1 To RangeToCheck.Rows.Count 'get highest and lowest difference
b(0) = x(1)(i, 1) - x(0)(i, 1)
If b(0) < b(1) Then b(1) = b(0)
If b(0) > b(2) Then b(2) = b(0)
Next
If Application.Sum(x(1)) < 100 Then b(0) = b(1) Else b(0) = b(2)
For i = 1 To RangeToCheck.Rows.Count
If b(0) = x(1)(i, 1) - x(0)(i, 1) Then
x(1)(i, 1) = x(1)(i, 1) + (0.5 + (b(0) > 0)) * 2
Exit For
End If
Next
Wend
For i = 1 To RangeToCheck.Rows.Count
If Application.Round(x(0)(i, 1), 0) <> x(1)(i, 1) Then
RangeToCheck.Cells(i).NumberFormat = """" & x(1)(i, 1) & "%"""
End If
Next
End Sub
It may need to change some parts... but basically it is working. Also it is not looking very well, but I am in a hurry right now... sorry.
If you have any questions, just ask... I'll reply as soon as possible ;)
Related
Hey I am thinking about a solution for my problem for hours now. I've searched the web, too and was able to find some approaches but none of them does work for my problem.
I have one cell with data that appears like this:
(Monday#100%[00:00-24:00]);(Tuesday#100%[00:00-24:00]);(Wednesday#100%[00:00-24:00]);(Thursday#100%[00:00-24:00]);(Friday#100%[00:00-24:00]);(Saturday#100%[00:00-24:00]);(Sunday#100%[00:00-24:00])
The problem is, that this string is not consistent. It could also look like this:
(Monday#125%[00:00-04:00]);(Monday#90%[04:00-08:00]);(Monday#90%[08:00-12:00]);(Monday#115%[12:00-16:00]);(Monday#120%[16:00-20:00]);(Monday#115%[20:00-24:00]);(Tuesday#125%[00:00-04:00]);(Tuesday#90%[04:00-08:00]);(Tuesday#90%[08:00-12:00]);(Tuesday#115%[12:00-16:00]);(Tuesday#120%[16:00-20:00]);(Tuesday#115%[20:00-24:00]);(Wednesday#125%[00:00-04:00]);(Wednesday#90%[04:00-08:00]);(Wednesday#90%[08:00-12:00]);(Wednesday#115%[12:00-16:00]);(Wednesday#120%[16:00-20:00]);(Wednesday#115%[20:00-24:00]);(Thursday#125%[00:00-04:00]);(Thursday#90%[04:00-08:00]);(Thursday#90%[08:00-12:00]);(Thursday#115%[12:00-16:00]);(Thursday#120%[16:00-20:00]);(Thursday#115%[20:00-24:00]);(Friday#125%[00:00-04:00]);(Friday#90%[04:00-08:00]);(Friday#90%[08:00-12:00]);(Friday#115%[12:00-16:00]);(Friday#120%[16:00-20:00]);(Friday#115%[20:00-24:00]);(Saturday#125%[00:00-04:00]);(Saturday#90%[04:00-08:00]);(Saturday#90%[08:00-12:00]);(Saturday#115%[12:00-16:00]);(Saturday#120%[16:00-20:00]);(Saturday#115%[20:00-24:00]);(Sunday#125%[00:00-04:00]);(Sunday#90%[04:00-08:00]);(Sunday#90%[08:00-12:00]);(Sunday#115%[12:00-16:00]);(Sunday#120%[16:00-20:00]);(Sunday#115%[20:00-24:00])
The structure of the string is
You have a day of the week followed by an "#"
after the at (#) you have a number that can be between 0 and 999
after that you have a time of the day in brackets []
You can have up to 6 different time frames of one day (not more, but less)
each time frame cluster is separated by ; and within normal brackets ()
So this is my starting position. What I want is to extract the numbers between # and % and list them chronologically (Monday to Sunday, hours of the day).
I was able to extract the number between # and % for each day of the week with this formula
=MID(B3;SEARCH("Monday#";B3)+7;SEARCH("%";B3)-SEARCH("Monday#";B3)-7)
But this only works if each day of the week is mentioned only ones.
I am out of ideas of how I could get it to work when having more time frames per day. Appreciate any help.
Thanks,
Ramon
Try an set of iterative searches, where the next search starts at the position found for the previous result. ie:
in cell B6 put =SEARCH("]",$B$3,B5+1)
in cell C6 put =MID($B$3,SEARCH("(",$B$3,B5+1)+1,SEARCH("#",$B$3,B5+1)-SEARCH("(",$B$3,B5+1)-1)
in cell D6 put =MID($B$3,SEARCH("[",$B$3,B5+1)+1,SEARCH("]",$B$3,B5+1)-SEARCH("[",$B$3,B5+1)-1)
in cell E6 put =MID($B$3,SEARCH("#",$B$3,B5+1)+1,SEARCH("%",$B$3,B5+1)-SEARCH("#",$B$3,B5+1)-1)
(Note that B5 needs to be empty (or 0) for this to start correctly)
You can then fill down as far as needed to pick up each term. If they are out of order, then you can order on column D then C (the time then the day).
This might get you started.
Sub Extractor()
Dim data() As String, i As Integer, rw As Integer
rw = 3
data = Split(Range("A1"), ";")
For i = 0 To UBound(data)
Range("A" & rw) = VBA.Mid$(data(i), 2, InStr(1, data(i), "#") - 2) 'Day
Range("B" & rw) = VBA.Mid$(data(i), InStr(1, data(i), "#") + 1, InStr(1, data(i), "%") - InStr(1, data(i), "#") - 1) '% number
Range("C" & rw) = VBA.Mid$(data(i), InStr(1, data(i), "[") + 1, InStr(1, data(i), "]") - InStr(1, data(i), "[") - 1) 'Time
rw = rw + 1
Next i
End Sub
Notes:
Assumes your string is in A1
Prints your data in columns A, B, and C starting in row 3
You can use Text-to-Columns feature of excel, with ) as a delimiter. Than transpose the columns into rows to get something like this:
(Monday#125%[00:00-04:00]
;(Monday#90%[04:00-08:00]
;(Monday#90%[08:00-12:00]
;(Monday#115%[12:00-16:00]
;(Monday#120%[16:00-20:00]
...
;(Sunday#115%[20:00-24:00]
Then apply formulas.
Edit:
Using just Find and Mid you can achieve this:
The idea behind find and mid is always the same.
Ex. for finding ( is =FIND("(";[Text]).
Ex. for extracting Day is =MID([Text];[#[Pos(]]+1;[#[Pos'#]]-[#[Pos(]]-1)
I've tried several different variations of the below code to try to find a method that may work. My last iteration left me at:
Dim D As Range
Dim x As Integer, c As Long, lre As Long
c = Range("A" & Rows.Count).End(xlUp).Row
lre = Sheets("Exception Report").Cells(Sheets("ExceptionReport").Rows.Count, "A").End(xlUp).Row
Range("D2:D" & lre).TextToColumns
Range("F2:F" & lre).TextToColumns
For x = c To 2 Step -1
If -0.1 < CDbl((Cells(x, "D").Value) - (Cells(x, "E").Value)) < 0.1 And -0.1 < CDbl((Cells(x, "F").Value) - (Cells(x, "G").Value)) < 0.1 Then
Cells(x, 1).EntireRow.Delete
End If
Next
The code in this part look at two pairs of cells, and I subtract one from the other in order to look at the difference. If it is within the range above (The -0.1 < code < 0.1) then it is to be deleted, basically leaving certain outliers. The text to columns part turns some of the data (which is stored as text) into number datatype so I can work with it for this purpose.
The problem I have been facing is that no matter the numbers used, it evaluates as true which then deletes the entire row, leaving the report blank, even though I know that there are rows which do not meet the criteria.
I've run out of ideas and my novice experience with excel has left me unable to debug this. Any help/comments/ideas?
You can't have multiple comparisons like a < b < c, you need to use a < b And b < c.
I am not exactly sure what happens but one of the <s is evaluated first and the result (True or False) is then compared to the last number which mixes things up.
Edit: the first < seems to be evaluated first, giving True or False since True is evaluated as -1 and False as 0 when being cast as Double, you always get True when checking <0.1.
I want to multiply two 100-Digit Numbers In Excel using matrix. The issue in Excel is that after 15-digit, it shows only 0. So, the output also need to be in a Matrix.
1st Number: "9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
2nd Number: "2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222"
Output: "22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222217777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777778"
This may be what OP was after. I thought I would try a naive multiplication method to see how long it would take to run. The answer is less than a second for two 100-digit numbers. You have to select the output range (i.e. A3:GR3 for a 200-digit result) and enter the formula containing the input ranges as an array formula using CtrlShiftEnter e.g.
=Multiply(A1:CV1,A2:CV2)
for two 100-digit numbers.
The method is basically just a simulation of school maths long multiplication, except that the intermediate rows are not stored but immediately added to the answer thus saving a lot of space.
The utility of it is obviously not that it is a replacement for the Karatsuba method, but it is a simple verifiable method which could be used for one-off calculations.
Currently limited to multiplication of rows containing more than one cell (so if you wanted to multiply by a single digit number, would have to enter it as e.g. 09).
Start of numbers
Middle of numbers
End of numbers
Function Multiply(rng1 As Variant, rng2 As Variant)
Dim arr() As Integer
Dim arrLength, r1Length, r2Length, carry, product, digit As Integer
Dim tot, totDigit, totCarry As Integer
Dim v1, v2 As Variant
v1 = rng1
v2 = rng2
r1Length = UBound(v1, 2)
r2Length = UBound(v2, 2)
arrLength = r1Length + r2Length
' Declare 1D array with enough space
ReDim arr(1 To arrLength)
' Loop over digits in first number starting from right
For i = r1Length To 1 Step -1
carry = 0
totCarry = 0
' Loop over digits in second number starting from right
For j = r2Length To 1 Step -1
' Calculate next digit in intermediate values (i.e. one row of long multiplication)
product = v1(1, i) * v2(1, j) + carry
digit = product Mod 10
carry = Int(product / 10)
' Calculate next digit in final values (i.e. totals line of long multiplication)
tot = arr(i + j) + digit + totCarry
arr(i + j) = tot Mod 10
totCarry = Int(tot / 10)
Next j
' Process final carry
arr(i) = carry + totCarry
Next i
' Return as an array
Multiply = arr
End Function
OK it might work like this:-
(1) You need to concatenate your numbers into a string because that's what you need as the input to your function. Native Excel won't do concatenation on arrays so you need a UDF like this one. So B2 contains
=concat(D1:G1)
(2) The output from the function is a string so you need to split it back into separate cells. You could use another UDF or a formula like this one copied across:-
=IF(COLUMNS($C3:C3)>LEN($B$3),"",VALUE(MID($B3,COLUMNS($C3:C3),1)))
So for the simple example it would look like this:-
But I might have got the wrong end of the stick completely.
Place your arrays into A1:A100 and B1:B100, then use three formulas:
1) In C2:C200 enter this as an array formula:
=MMULT(IFERROR(INDEX(A1:A100*TRANSPOSE(B1:B100),(ROW(INDIRECT("1:"&ROWS(A1:A100)*2-1))>0)+TRANSPOSE(ROW(INDIRECT("1:"&ROWS(A1:A100))))-1,MOD(ROW(INDIRECT("1:"&ROWS(A1:A100)*2-1))-TRANSPOSE(ROW(INDIRECT("1:"&ROWS(A1:A100)))),ROWS(A1:A100)*2-1)+1),0),SIGN(A1:A100+1))
2) In D1 enter =C1+INT(D2/10) and fill down to D200.
3) In E1 enter =MOD(D1,10) and fill down to D200.
E1:E200 will contain the answer.
I have 1 column with about 60 cells with values or different length. Each (or at least most) of the values have a numeric characters in the value. I want to split the columns cells into more columns which I normally would do with the 'tekst to columns' function of excel.
But this function does not have an advanced option of splitting the value at the first numeric character. splitting based on spaces, comma etc. is possible but this does not help me.
Is there any way to divide the cells into 2 columns at the first number in the cell value?
I have looked at numerous other questions but none of them (or other internet fora) have helped me to split the value at the first number of the cell value.
Thanks #quantum285 for the answer. This routine works if the string contains one number. I changed the teststring to firstpart323secondpart.
then part1 returns 'firstpart32' and part2 return secondpart.
I tried to understand what happens in this code, please correct me if I'm wrong:
First, the lenght of the string is determined.
Secondly, for each position in this string is checked if it is numeric or not. But this check is dan from right to left? So in case of firstpart323secondpart: the length is 22.
then isnumeric() checks for every position from 1 to 22 if it is numeric and stops when it finds a number?
If so, part 1 is the the tekst before the value i, where i is the first position from right to left in the string where a number is found.
and part 2 is then the string on the right from this same position.
However, I am looking for a routine which find the first position from left to right (or the last position from right to left) where a number is, ...
So I changed the routine now, simply adjusting the for i = 1 to line:
Sub test()
For j = 4 To Cells(Rows.Count, 4).End(xlUp).Row
For i = Len(Cells(j, 4)) To 1 Step -1
If IsNumeric(Mid(Cells(j, 4), i, 1)) Then
Cells(j, 5) = Left(Cells(j, 4), i - 1)
Cells(j, 6) = (Right(Cells(j, 4), Len(Cells(j, 4)) - i + 1))
End If
Next i
Next j
End Sub
this almost perfectly works (except for a few cells which have multiple number combinations in the value (like: soup 10g 20boxes). But as these are only a few, I can adjust them by hand.
Thanks!
Sub test()
testString = "firstpart3secondpart"
For i = 1 To Len(testString)
If IsNumeric(Mid(testString, i, 1)) Then
part1 = Left(testString, i - 1)
part2 = (Right(testString, Len(testString) - i))
End If
Next i
MsgBox (part1)
MsgBox (part2)
End Sub
Use something like this within your loop.
I need some math help, or some excel help if that works better. I have a set of data points and I need to calculate the intermediate data. The table below are the known data points, what I need to know is if I enter 1200 ft, what MEG is available.
FOOTAGE MEG
1000 19.3
2000 20.66
3000 21.328
4000 21.398
5000 20.976
6000 20.155
7000 19.023
8000 17.658
9000 16.133
10,000 14.513
11,000 12.854
12,000 11.208
13,000 9.617
14,000 8.117
15,000 6.736
16,000 5.493
17,000 4.411
18,000 3.487
19,000 2.724
20,000 2.114
I have entered these into excel and proceded to find a formula that their chart believes to be the answer to my questions. The formula they give is
y = 8E-12x3 - 3E-07x2 + 0.0018x + 18.218
This actually gets me really good results for anything under 12k feet. After that the results stray further and further from accurate until I start getting negative numbers after 18k feet.
I tried entering more orders for it to calculate against, but that just made things worse.
Would I be better off splitting the chart in 2 (>10k ft and <10k ft) and using 2 formulas, or is there a good solution available using the whole chart?
Precision is tremendously important. In fact, there are several serious problems here with what you have done.
Mere use of coefficients with a single digit of precision will cause terrible problems. Remember that x is as large as 20000. Cubing a number of that size will be a huge number. Now, multiply it by a number on the order of 8e-12, and what do you get?
Oh, by the way, the actual value of those coefficients should be closer to
[8.38044124105504e-12 -2.95337111670131e-07 0.00176948515975282 18.2176584107327]
So does this make a difference?
8e-12*20000^3
ans =
64
8.38044124105504e-12*20000^3
ans =
67.0435299284403
It DOES make a difference, a serious one.
You MIGHT choose to use simple linear interpolation, but the cubic is a bit smoother. Beware extrapolation, as a cubic will do strange things if you try it. In fact though, the cubic polynomial has a significant amount of lack of fit. You can do significantly better using a 4th order polynomial, as long as you are careful to scale the independent variable (footage) by dividing by 10000.
a4 = -3.02325078929022
a3 = 21.0780945560741
a2 = -46.9692303618201
a1 = 26.3111163470058
a0 = 17.1162276831784
MEG = a0 + a1*footage/10000 + a2*(footage/10000)^2 +
a3*(footage/10000)^3 + a4*(footage/10000)^4
Note the importance of scaling by 10000 (or at least a number that is chosen to transform your numbers so they are on the order of 1 or so.)
I'd not go much past that point though in terms of increasing the order of the fit.
Can I suggest you use a small VBA script, which uses linear interpolation to pull out a value from your list:
Public Function Linterp(Tbl As Range, x As Double) As Variant
' linear interpolator / extrapolator
' Tbl is a two-column range containing known x, known y, sorted x ascending
Dim nRow As Long
Dim iLo As Long, iHi As Long
nRow = Tbl.Rows.Count
If nRow < 2 Or Tbl.Columns.Count <> 2 Then
Linterp = CVErr(xlErrValue)
Exit Function '-------------------------------------------------------->
End If
If x < Tbl(1, 1) Then ' x < xmin, extrapolate from first two entries
iLo = 1
iHi = 2
ElseIf x > Tbl(nRow, 1) Then ' x > xmax, extrapolate from last two entries
iLo = nRow - 1
iHi = nRow
Else
iLo = Application.Match(x, Application.Index(Tbl, 0, 1), 1)
If Tbl(iLo, 1) = x Then ' x is exact from table
Linterp = Tbl(iLo, 2)
Exit Function '---------------------------------------------------->
Else ' x is between tabulated values, interpolate
iHi = iLo + 1
End If
End If
Linterp = Tbl(iLo, 2) + (Tbl(iHi, 2) - Tbl(iLo, 2)) * (x - Tbl(iLo, 1)) / (Tbl(iHi, 1) - Tbl(iLo, 1))
End Function
You call this from your sheet with something like:
=Linterp(A1:b10, 1200)
You can easily tweak the code to adjust how you want values outside of the range to be handled.
On a slightly different note, you may also be interested in this http://www.codecogs.com/excel_render which can draw out your equations.