Validate Alphabetic Characters in a Cell - excel

I am trying to validate ID's that follow specific conditions. These are:
10 character length
1st character = numerical
2nd character = alphabetical
3rd-8th character = numerical
9th character = "/"
10th character = alphabetical
e.g. 1A234567/B
I am have no problem with most of the formula, but I am stuck how to validate the alphabetic characters at character 2 & 10.
My current formula (excluding the formulas for the alphabetic characters):
=IF(AND(LEN(F2)=10,ISNUMBER(--MID(F2,3,6)),MID(F2,9,1)="/"),"Valid","Invalid")
I hope someone can help ! Thanks.

One, IMHO fun, way to do this is through FILTERXML(). Yes, it's probably more verbose than nested AND() statements, but you could try:
Formula in B1:
=NOT(ISERROR(FILTERXML("<t><s>"&A1&"</s></t>","//s[string-length()=10][concat(substring(.,1,1), substring(., 3,6))*0=0][translate(concat(substring(., 2, 1), substring(., 10)), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ','')=''][substring(.,9,1)='/']")))
[string-length()=10] - Test if total length equals 10;
[concat(substring(.,1,1), substring(., 3,6))*0=0] - Test if when we concatenate the 1st character with the 3-8th character equals 0 when multiplied by 0. Meaning: we validate that these characters are numeric;
[translate(concat(substring(., 2, 1), substring(., 10)), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ','')=''] - Validate that when we translate all uppercase characters to nothing in the concatenated string from the 2nd and last character equals an empty string;
[substring(.,9,1)='/'] - Validate that the 9th character equals a forward slash.
Note:
WAAR is Dutch for TRUE;
You can use this in cell-validation when you would apply a rule;
FILTERXML() is case-sensitive;
More on FILTERXML() and the usage of it here.

Just an excuse for me to try out the shiny new Advanced Formula Environment
You can create more extensive lambdas like this:
=LAMBDA(cell, lower, upper,
LET(
length, LEN(lower),
seq, SEQUENCE(1, length),
splitCell, MID(cell, seq, 1),
splitLower, MID(lower, seq, 1),
splitUpper, MID(upper, seq, 1),
AND(
SUM(
(splitCell >= splitLower) *
(splitCell <= splitUpper)
) = length,
LEN(cell) = length
)
)
)
EDIT
The formula above would be case insensitive. To make it cases sensitive, you could use CODE, but this would need to be wrapped in IFERROR:
=LAMBDA(cell, lower, upper,
IFERROR(
LET(
length, LEN(lower),
seq, SEQUENCE(1, length),
splitCell, CODE(MID(cell, seq, 1)),
splitLower, CODE(MID(lower, seq, 1)),
splitUpper, CODE(MID(upper, seq, 1)),
AND(
SUM(
(splitCell >= splitLower) *
(splitCell <= splitUpper)
) = length,
LEN(cell) = length
)
),
FALSE
)
)

Related

Excel Formula to Insert Spaces Before Capital Letters and Numbers

In column A, starting with A1, I have a set of database column names which are Pascale case and without spaces. I'd like to use an Excel formula in column B to insert spaces before each Capital letter or number. Ideally any consecutive capital letters or numbers would remain together. I've done this in the past with C#, but on this project, I can't even use VBA macros. Example output:
Can this, or something close, be achieved using only formulas?
This is pretty hard, but with ms365 doable with the give sample data:
Formula in B1:
=MAP(A1:A10,LAMBDA(v,TRIM(REDUCE(v,SEQUENCE(LEN(v),,LEN(v),-1),LAMBDA(a,b,LET(x,MAKEARRAY(26,3,LAMBDA(r,c,CHOOSE(c,CHAR(r+64),CHAR(r+96),r-0))),y,MID(a,b,1),z,MID(a,b+1,1),r,BYCOL(x,LAMBDA(c,SUM(EXACT(c,y)+EXACT(c,z)))),IF(MAX(r)=1,LEFT(a,b-1)&IF((CONCAT(r)="110")*(EXACT(UPPER(y),y))," "&y,y&" ")&RIGHT(a,LEN(a)-b),a)))))))
Maybe others have shorter solutions...
Just for fun, this uses a single Reduce but I have defined some auxiliary functions. I put them in a module called 'is' in the Advanced Formula Environment so their full names are Is.Upper, Is.Lower and Is.Digit:
Upper=lambda(c,if(c="",false,and(code(c)>64,code(c)<91)));
Digit=lambda(c,if(c="",false,and(code(c)>47,code(c)<58)));
Lower=lambda(c,if(c="",false,and(code(c)>96,code(c<123))))
=REDUCE(LEFT(A1,1),SEQUENCE(1,LEN(A1)-1,2),LAMBDA(a,c,a&IF(OR(AND(is.Digit(MID(A1,c,1)),NOT(is.Digit(MID(A1,c-1,1)))),AND(is.Upper(MID(A1,c,1)),OR(NOT(is.Upper(MID(A1,c-1,1))),is.Lower(MID(A1,c+1,1)))))," ","")&MID(A1,c,1)))
This is how the main formula looks in the Advanced Formula Environment:
=REDUCE(
LEFT(A2, 1),
SEQUENCE(1, LEN(A2) - 1, 2),
LAMBDA(a, c,
a &
IF(
OR(
AND(
is.Digit(MID(A2, c, 1)),
NOT(is.Digit(MID(A2, c - 1, 1)))
),
AND(
is.Upper(MID(A2, c, 1)),
OR(
NOT(is.Upper(MID(A2, c - 1, 1))),
is.Lower(MID(A2, c + 1, 1))
)
)
),
" ",
""
) & MID(A2, c, 1)
)
)
Note - assumes string length>1.

How to generate a random alphanumeric string with a formula in Excel (or Google Sheets or LibreOffice)

I'm trying to generate a random 8 character alphanumeric string in Excel (or Google Sheets or Libreoffice, which both have the same challenge) using a formula. I'd like to get something like this:
6n1a3pax
I've tried various formulae including ones like this which generate the ASCII characters for individual random numbers between an upper and lower number:
=CHAR(RANDBETWEEN(65,90)) & CHAR(RANDBETWEEN(65,90)) & CHAR(RANDBETWEEN(65,90)) &CHAR(RANDBETWEEN(65,90))& CHAR(RANDBETWEEN(65,90)) & CHAR(RANDBETWEEN(65,90)) & CHAR(RANDBETWEEN(65,90)) & CHAR(RANDBETWEEN(65,90))
However, they're lengthy, you have to repeat the RANDBETWEEN() function multiple times inside a formula, and you can't choose both "alpha" and "numeric" in the same RANDBETWEEN().
Is there any easy way to do this in Excel, Google Sheets or LibreOffice Calc? If a solution works in one and not in the others then great if you can mention which one(s).
(N.B. This is not a duplicate of questions about how to stop recalculation of randomisation functions in Excel)
in GS try:
=LAMBDA(x, x)(DEC2HEX(RANDBETWEEN(0, HEX2DEC("FFFFFFFF")), 8))
if that's not enough and you need
A-Z char 65-90
a-z char 97-122
0-9 char 48-58
=JOIN(, BYROW(SEQUENCE(8), LAMBDA(x, IF(COINFLIP(), IF(COINFLIP(),
CHAR(RANDBETWEEN(65, 90)), CHAR(RANDBETWEEN(97, 122))), RANDBETWEEN(0, 9)))))
frozen:
=LAMBDA(x, x)(JOIN(, BYROW(SEQUENCE(8), LAMBDA(x, IF(COINFLIP(), IF(COINFLIP(),
CHAR(RANDBETWEEN(65, 90)), CHAR(RANDBETWEEN(97, 122))), RANDBETWEEN(0, 9))))))
alternative (with better distribution):
=JOIN(, BYROW(SEQUENCE(8), LAMBDA(x, SINGLE(SORT(CHAR({
SEQUENCE(10, 1, 48);
SEQUENCE(26, 1, 65);
SEQUENCE(26, 1, 97)}),
RANDARRAY(62, 1), )))))
or frozen:
=LAMBDA(x, x)(JOIN(, BYROW(SEQUENCE(8), LAMBDA(x, SINGLE(SORT(CHAR({
SEQUENCE(10, 1, 48);
SEQUENCE(26, 1, 65);
SEQUENCE(26, 1, 97)}),
RANDARRAY(62, 1), ))))))
for more see: stackoverflow.com/questions/66201364
LibreOffice Calc 7.x:
A non-volatile option for LibreOffice Calc 7.x is the use of the RANDBETWEEN.NV() function:
Formula in A1:
=CONCAT(IF({1,2,3,4,5,6,7,8},MID("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",RANDBETWEEN.NV(1,62),1),))
Note that using ROW(1:8) would still force recalculation when any value in rows 1-8 have been made (thus volatile):
=CONCAT(IF(ROW(1:8),MID("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",RANDBETWEEN.NV(1,62),1),))
Excel ms365:
Unfortunately there is, AFAIK, not a non-volatile Excel equivalent to this function. If volatility is not a problem, then try:
=CONCAT(MID("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",RANDARRAY(8,,1,62,1),1))
Here's my take, for Google Sheets:
=lambda(_,
_
)(
lambda(
numWords, wordLength, charRegex, ascii,
lambda(
alphabet,
map(
sequence(numWords),
lambda(_,
concatenate(
map(
sequence(wordLength),
lambda(_,
mid(alphabet, randbetween(1, len(alphabet)), 1)
)
)
)
)
)
)(concatenate(filter(ascii, regexmatch(ascii, charRegex))))
)(10, 8, "[0-9a-zA-Z]", arrayformula(char(sequence(127))))
)
The formula will generate 10 passwords of 8 characters each from an alphabet that includes lower and upper case letters, and digits.
To choose which characters to include in the alphabet, replace [0-9a-zA-Z] with another regex like [0-9a-z!#$%&/] or [-!#$%&/\w]. Note that you may need to \escape any regex special characters there.
The pattern avoids the non-uniform distribution issues that plague some of the solutions presented in this thread. The ones that use coinflip() or isodd(rand()*N) will give results that overrepresent smaller sub alphabets like 0-9. The ones that use sort() will not repeat any chars in the result, which is not optimal.
It's possible to do this in Excel using a combination of the following functions:
SEQUENCE() VSTACK() RANDARRAY() CHAR() INDEX() TEXTJOIN()
Unfortunately this doesn't work in LibreOffice (at the moment) as it does not have the SEQUENCE() function. It does not work in Google Sheets as the RANDARRAY() function only takes 2 parameters and the VSTACK() function does not exist, although you can use braces and a semicolon, e.g. {SEQUENCE(26,1,97,1);SEQUENCE(10,1,48,1)}.
Here's the formula you need:
Upper-case e.g "413BK5S0": =TEXTJOIN("",1,INDEX(CHAR(VSTACK(SEQUENCE(26,1,65,1),SEQUENCE(10,1,48,1))),RANDARRAY(8,1,1,36,TRUE)))
Lower-case e.g. "b8etbno8": =TEXTJOIN("",1,INDEX(CHAR(VSTACK(SEQUENCE(26,1,97,1),SEQUENCE(10,1,48,1))),RANDARRAY(8,1,1,36,TRUE)))
The following explanation for each function:
SEQUENCE() - a sequence of e.g. 26 numbers, in 1 column, starting at number 65, increasing by 1 each time (with the second incidence of the function being 10 numbers starting at 48)
VSTACK() - combine the 2 SEQUENCE() formulae into 1 array (sequence) of numbers
CHAR() - the ASCII character associated with a decimal ASCII number (where the decimal number is generated by the SEQUENCE() function) - see https://www.asciitable.com/
RANDARRAY() - an array of 8 random numbers, 1 column wide, minimum number 1, maximum 36
INDEX() - the value from each element within the sequence of characters, where each of 8 element numbers is provided by RANDARRAY()
TEXTJOIN() - join the values in an array together into one cell, with no separator and ignoring empty values
What do you think of something like this?
=CONCATENATE(BYROW(SEQUENCE(8),LAMBDA(e,IF(ISODD(ROUNDUP(RAND()*10)),CHAR(RANDBETWEEN(65,90)),ROUNDDOWN(RAND()*10)))))
If you want to include lower case, you can do a similar logic:
=CONCATENATE(BYROW(SEQUENCE(8),LAMBDA(e,IF(ISODD(ROUNDUP(RAND()*10)),IF(ISODD(ROUNDUP(RAND()*10)),CHAR(RANDBETWEEN(65,90)),CHAR(RANDBETWEEN(97,122))),ROUNDDOWN(RAND()*10)))))
The logic is the next one: what I'm doing is with ISODD(ROUNDUP(RAND()*10) generating a random number between 1 and 10 and checking if it's odd. If it is, it generates a letter or else it generates a number. With CONCATENATE(BYROW(SEQUENCE(8)... I'm doing this 8 times and concatenating them. What I just added was a second "random and odd" time when it's time to generate a letter so you can have upper and lower case

Excel 2013 - Comparing Two Cells

I am comparing two corresponding columns of data. In the first row, it passes because variable A and variable B are in column 2. It does not have to contain the variable starting with C, but it can only be any of the letters contained in cell 1:1. For row three, it does not pass because variable starting with A is not an option within cell 3:1. What conditional formatting would I do for a large data set in Excel 2013. That version of excel is all I have available.
If you had excel 365, it would be much easier. Using 2013, you either have to use FILTERXML to isolate the variables, or hard-code for a certain number of variables. Hard-coding gets long very fast.
I can't check in excel 2013, but it would probably look something like this:
=SUM(--ISERROR(
VLOOKUP(
FILTERXML("<x><y>"&SUBSTITUTE(B1,CHAR(10),"</y><y>")&"</y></x>","//y"),
FILTERXML("<x><y>"&SUBSTITUTE(A1,CHAR(10),"</y><y>")&"</y></x>","//y"),
1,FALSE)
))>0
Where the first FILTERXML creates an array of variables from cell B1, the second from cell A1. VLOOKUP finds matches for items in column B to items in column A, and SUM(--ISERROR(... counts how many non-matches are found.
For excel 365 it could look like this:
=LET(
Txt_1, $A1,
Txt_2, $B1,
Delim, CHAR(10),
SeqChar1, SEQUENCE(LEN(Txt_1)),
CharArr1, MID(Txt_1, SeqChar1, 1),
CharVarStartArr1, FILTER(SeqChar1, IF((CharArr1 = Delim) + (SeqChar1 = 1), TRUE, FALSE)),
CharVarLenArr1, FILTER(SeqChar1, IF((CharArr1 = Delim) + (SeqChar1 = LEN(Txt_1)), TRUE, FALSE)) - CharVarStartArr1 + 1,
VarArr1, SUBSTITUTE(MID(Txt_1, CharVarStartArr1, CharVarLenArr1), Delim, ""),
SeqChar2, SEQUENCE(LEN(Txt_2)),
CharArr2, MID(Txt_2, SeqChar2, 1),
CharVarStartArr2, FILTER(SeqChar2, IF((CharArr2 = Delim) + (SeqChar2 = 1), TRUE, FALSE)),
CharVarLenArr2, FILTER(SeqChar2, IF((CharArr2 = Delim) + (SeqChar2 = LEN(Txt_2)), TRUE, FALSE)) - CharVarStartArr2 + 1,
VarArr2, SUBSTITUTE(MID(Txt_2, CharVarStartArr2, CharVarLenArr2), Delim, ""),
OR(IF(ISERROR(XLOOKUP(VarArr2, VarArr1, VarArr1, NA())), TRUE, FALSE))
)
SeqChar1 creates an array of index locations for each string character using SEQUENCE.
CharArr1 creates an array of (single) characters. I'm using MID with array SeqChar1 to output the results as an array.
CharVarStartArr1 is an array with the starting point of each variable. This is done by getting rid of every position that doesn't correspond to a delim character. I also included the first character or we would be missing the first variable.
CharVarLenArr2 finds the approximate string length by subtracting the starting index from the index of the next variable. I included the index of the last string character to get the length for the last variable.
VarArr1 is an array containing each variable. Every variable but the first one also contains the delim character, so I remove them with SUBSTITUTE.

Extract and add sub strings into strings

The context here is that I'm trying to identify phone number patterns of a big messy column, and format them as this:
(CC) NNNN-NNNN
CC being two digit area code
There may be two or more numbers in the same cell(They need to remain in the same cell unfortunately), and need to be as follow :
(CC) NNNN-NNNN / (CC) NNNN-NNNN
The numbers are just raw digits, no spaces or characters, but are as TEXT and need to remain so because of the 15 digit limit on Excel
Now, I'm having problem with two cases
Case 1:
Two phones and one area code (18 digits)
Example : CCNNNNNNNNNNNNNNNN
What I need is a function that takes the first two characters from this string and add them at the eleventh spot of said string, resulting in a 20 char string
Case 2:
One phone and two same area code(12 digits)
CCCCNNNNNNNN
This one just need to remove the first two characters
Tried this way for case 1, but ended up with a 22 string char? not sure where I went wrong
s1 = Mid(Cells(j, 3), 1, 2)
s2 = Mid(Cells(j, 3), 3, 10)
s3 = Mid(Cells(j, 3), 11, 18)
s4 = s1 & s2 & s1 & s3
The big caveat here is the formatting of the existing data.... how do you know what digits are "area code" and what digits are phone number? You can do what you are trying to do here with Excel formulas without any VBA... For Case 1 you can use the formula below, replacing "A1" with the appropriate cell reference.
="("&MID(A1, 1, 2)&") "&mid(A1,3,4)&"-"&mid(a1,7,4)&" / ("&MID(A1, 1, 2)&") "&mid(A1,11,4)&"-"&mid(a1,15,4)
For Case 2 you can use the formula below.
="("&MID(A1, 1, 2)&") "&mid(A1,5,4)&"-"&mid(a1,9,4)
Does your solution have to be in vba?
The third parameter in Mid is the length of the extracted substring, not its final index. If it is omitted - the selection goes to the end of the string. The selection also goes to the end of the string if the length parameter exceeds the number of possible characters. s2 = Mid(Cells(j, 3), 3, 10) was thus of length 10 rather than the expected 8. Your case 1 would look something like this:
Sub test()
Dim s As String, t As String
s = "CCNNNNNNNNNNNNNNNN"
t = Mid(s, 1, 10) & Mid(s, 1, 2) & Mid(s, 11)
Debug.Print t
End Sub
Which has output:
CCNNNNNNNNCCNNNNNNNN
For your two cases:
=IF(LEN(A1)=18,REPLACE(A1,11,0,LEFT(A1,2)),IF(LEN(A1)=12,MID(A1,3,10)))
Not knowing the other formats or desired results, it is hard to expand on the formula. The formula will return FALSE if the length is other than 12 or 18.

SI-prefixes for number format in MS Excel

Does anybody know if it is possible to show numbers in MS Excel with SI-prefixes?
I'd like to have
... 1 n, 1 µ, 1 m, 1, 1 k, 1M, 1 G, ...
instead of scientific format
... 1E-09, 1E-06, 1E-03, 1, 1E+03, 1E+06. 1E+09, ...
Perhaps adding an unit like V (volts), F (farad) etc.
I would be perfect, if the cell would still contain the number and not a string, so it can easily be changed to another format (back to scientific or whatever)
You can do something like this, which I got from Millions & Thousands Custom Number Formatting :
[>=1000000] #,##0.0,," MΩ";[<1000000] #,##0.0," kΩ";General
400 renders as 0.4 kΩ (probably not what you want)
4000 renders as 4.0 kΩ
40e3 renders as 40.0 kΩ
40e6 renders as 40.0 MΩ
but you can probably add more clauses to cover other ranges. Nevermind, you can't.
You can also use LOG and CHOOSE to keep it in a single formula and reasonably compact.
=ROUND(
E10 / (1000 ^ INT(LOG(ABS(E10),1000)) )
,0
) & CHOOSE(
INT(LOG(ABS(E10),1000)) + 6
,"f","p","n","µ","m","","k","M","G","T","P"
)
In this formula:
E10 (referred to 3 times) is the cell containing the raw value.
ROUND formats number for display, here rounding to no decimals (0).
INT(LOG(ABS(E10),1000)) is the prefix index -5 through +5.
CHOOSE is the prefix to use (needs positive index, hence + 6).
No solution will work better than scientific notation.
If you use custom number formats, then you would have to enter them manually (or with VBA) such that they will mask the actual content of the cell.
For instance, if you want to display the following format pairs:
1 n 1E-09
1 µ 1E-06
1 m 1E-03
1 1
1 k 1E+03
1 M 1E+06
1 G 1E+09
If you have 0.001, you would have to set the format as "1 m" -- this will mask the number, so if you have 0.002 you would have to set it as "2 m" -- if you changed it to 0.004 it would still display 2 m as a result. This obviously isn't ideal.
You could set it up as a two-column sheet, where you have the values in the left, and use a formula to display with units on the right, but then you end up not being able to do math with the formatted values.
So basically, the answer is "no", it isn't possible.
You could theoretically write a VBA script that will automatically change the visible contents according to the cell contents whenever a number is changed, but the script would be bulky and would cause serious trouble to whoever you sent to if they had macros off. It would also require all sorts of corner cases depending on if you wanted numbers formatted 'normally' in certain cells. So while it may be theoretically possible, it is practically impossible
It is possible, though bulky using a conversion table and the match and index functions.
From a conversion table like this (2 columns):
1.E+15 P
1.E+12 T
1.E+09 G
1.E+06 M
1.E+03 k
1.E+00
1.E-03 m
1.E-06 µ
1.E-09 n
1.E-12 p
1.E-15 f
You could then perform the following translation
3.68437E+11 --> 368.44 G
If you have the conversion table in columns A and B
and the unformatted number in cell G1
H1
=G1/INDEX(A:A,MATCH(G1,$A:$A,-1)+1)
I1
=INDEX($B:$B,MATCH(G1,$A:$A,-1)+1)
Then the proper numerals will display in column H with the suffix/prefix in column I.
It is still ponderous, and should only be used for final output since calculations from the modified numbers will have to include a reverse translation.
There's no way I know of to do this as a number format (where you can then use the formatted number as you would any other numeric value for subsequent calculation), but for simply presenting the number using SI prefixes, here's the formula I use. It takes the formula in the specified cell (usually next to it, in this case E28), scales the number, rounds to the specified number of significant figures (in this case 3), appends the appropriate SI prefix, and then appends the specified unit (in this case 'F' for Farads). The advantage here is that the formula is self-contained and doesn't require any external reference tables. This formula works for femto (10^-15) through Tera (10^12), but can easily be expanded for additional prefixes
=CONCAT(
ROUND(
IF(E28>1E12, E28/1E12,
IF(E28>1E9, E28/1E9,
IF(E28>1E6, E28/1E6,
IF(E28>1E3, E28/1E3,
IF(E28>1, E28,
IF(E28>1E-3, E28*1E3,
IF(E28>1E-6, E28*1E6,
IF(E28>1E-9, E28*1E9,
IF(E28>1E-12, E28*1E12,
E28*1E15
) ) ) ) ) ) ) ) ),
3 +N("This is the number of significant digits to round to")
-(1+INT(LOG10(ABS(
IF(E28>1E12, E28/1E12,
IF(E28>1E9, E28/1E9,
IF(E28>1E6, E28/1E6,
IF(E28>1E3, E28/1E3,
IF(E28>1, E28,
IF(E28>1E-3, E28*1E3,
IF(E28>1E-6, E28*1E6,
IF(E28>1E-9, E28*1E9,
IF(E28>1E-12, E28*1E12,
E28*1E15
) ) ) ) ) ) ) ) ) ))))
),
IF(E28>1E12, "T",
IF(E28>1E9, "G",
IF(E28>1E6, "M",
IF(E28>1E3, "k",
IF(E28>1, "",
IF(E28>1E-3, "m",
IF(E28>1E-6, "µ",
IF(E28>1E-9, "n",
IF(E28>1E-12, "p",
"f"
) ) ) ) ) ) ) ) ),
"F" +N("This is the unit symbol that will be appended to the end")
)
If you want to round to a fixed number of decimal figures as opposed to significant figures, the formula is a little simpler:
=CONCAT(
ROUND(
IF(E28>1E12, E28/1E12,
IF(E28>1E9, E28/1E9,
IF(E28>1E6, E28/1E6,
IF(E28>1E3, E28/1E3,
IF(E28>1, E28,
IF(E28>1E-3, E28*1E3,
IF(E28>1E-6, E28*1E6,
IF(E28>1E-9, E28*1E9,
IF(E28>1E-12, E28*1E12,
E28*1E15
) ) ) ) ) ) ) ) ),
3 +N("This is the number of decimal digits to round to")
),
IF(E28>1E12, "T",
IF(E28>1E9, "G",
IF(E28>1E6, "M",
IF(E28>1E3, "k",
IF(E28>1, "",
IF(E28>1E-3, "m",
IF(E28>1E-6, "µ",
IF(E28>1E-9, "n",
IF(E28>1E-12, "p",
"f"
) ) ) ) ) ) ) ) ),
"F" +N("This is the unit symbol that will be appended to the end")
)
Note that I've written all of the scaling constants in exponential notation, Excel will change these to plain numbers when you enter the formula. By using a relative cell reference, it's pretty easy to copy and paste the formula around where you need it.
The formula could be condensed into a single block of IF/CONCAT/ROUND statements, but arranging it as I've done here separates out the rounding constant into a single point in the formula, making it easier to change.
Just select the cell or range of cells you want to contain the given symbol. Right click on the cell and select FORMAT CELLS. Select the NUMBER format on the left, enter decimal places, etc on the right. Now go all the way down the list of your format options on the left and select CUSTOM. (IMPORTANT: Do NOT select ANY custom format options on the right.) Left click in the box just below TYPE: and above the list of custom format options. (This box displays your current selected format. {0.00 if you selected the default number format} You want to keep this formatting AND add additional formatting.) Click to the right of 0.00 and type the following: " μ" Click OKAY and you may enter your data as normal. Formatting cells has no impact on the values you enter. You can perform all functions as normal. I am attaching a pic where I used the same instructions to apply litters and the greek MU notation to values and performed some basic calculations without impeding Excel's ability to function.Special Notation in Excel
This is a limited answer for Google Sheets, using actual number formats instead of expressions that output text.
Spinning off from endolith's answer, I settled on this:
[>=1E6] #,##0.0,,"M";[>1E3] #,##0.0,"K";0.#####################
It works on numbers from 1 to <1E16, though can't be expanded to units above M. Doesn't work for negative numbers or fractional numbers. It is limited by the number of conditional sections Google Sheets is able to parse.
Docs: https://developers.google.com/sheets/api/guides/formats#number_format_patterns
' Hans Wolfgang Schulze 20190921, cause I always need this and need to write it again cause I forgot where I saved it.
' Paste this into Excel's Macro Editor (F11) and use from any cell.
' Copyleft 2019. Please include original author's name in all derivative works.
'
' Note that the conversions in this code is assuming normal Base 10, and not Binary 1024. Lots of code has to change.
' Currently recognizes numbers like "-123123.123G" or "123E15" with or without actual units.
' Special case of Excel's "-" nothing equals 0.
' Assumes, if exists, that the rightmost character is the SI exponent designation - See expS below.
' Usage: =DSci("45e9k") gives "4.5E12" as an answer.
Const expS = "-qryzafpnum KMGTPEZYRQ" ' https://en.wikipedia.org/wiki/Metric_prefix
Function DSci(inputS As String) As Double
Dim which As Integer
which = InStr(expS, Right(inputS, 1))
whichUnitary = InStr(expS, " ") ' offset into expS for " " unity value
If which = 0 Then
which = InStr("----F-Nµ- k-gt-e", Right(inputS, 1)) ' try alt case and form that aren't obscure ie k=K or m!=M
End If
If which > 0 Then ' has a terminating exponential character. 1 is not found.
If which = 1 Then ' "-"
DSci = 0 ' excel nothing value (0)
Else ' convert only the left side of input
DSci = CDbl(Left(inputS, Len(inputS) - 1)) * 10# ^ ((which - whichUnitary) * 3#) ' fix for Binary K's
End If
Else
DSci = CDbl(inputS) ' convert whole string instead ' special case devide by 1.024 for each 1000 for Binary K's
End If
End Function
' Formats to SI convention 10 ^ (-30 ... +30) and recent suggested expansions
' Usage =Sci(5.531e9, "B") gives string of "5.531GB"
' Significant digits are suggested as 4, can be any positive number.
Function Sci(value As Double, optionalUnit As String, Optional significant As Integer = 4) As String
Dim mant As Double, exp As Double, rank As Integer
rankUnitary = InStr(expS, " ") ' offset into expS for " " unity value
If value = 0 Then exp = 0 Else exp = Log(value) / Log(10#) ' nDigits
mant = value / (10# ^ exp) '
While mant >= 999.9999999999 ' don't want 2000K, rather 2M. Change to 1023.9999999999# for Binary K's
exp = exp + 3#
mant = mant / 1000# ' change to 1024# for binary K's etc.
Wend
rank = Int((exp + 0.0000000000001) / 3#) ' should be >1E-300 or so? Why not? 3 != 3#? More changes for Binary K's ?
mant = mant * 10# ^ (-rank * 3# + exp) ' adjust mantussa after de-ranking. Change?? for Binary K's
If Abs(rank) >= rankUnitary Then ' outside of +/- yY bounds
expChar = "?" ' what do you call it then? Not defined.
Else
expChar = Mid(expS, rank + rankUnitary, 1) ' add SI
End If
Sci = Left(mant, Abs(significant)) ' don't allow negative numbers, pretend they are positive lengths
If Right(Sci, 1) = "." Then Sci = Left(Sci, Len(Sci) - 1) ' lop off right DP
Sci = Sci & " " & expChar & optionalUnit
End Function

Resources