How to check if String is Decimal?

I suppose you could make your own isNumeric() method to also cover decimal values:

public class StringUtilities{
    public static Boolean isNumeric(String s){
        Boolean ReturnValue;
        try{
            Decimal.valueOf(s);
            ReturnValue = TRUE; 
        } catch (Exception e) {
            ReturnValue = FALSE;
        }
        return ReturnValue;
    }
}

Sample runs:

list<String> slist = new list<String>{
  '12',       // TRUE
  '12.2',     // TRUE
  'string',   // FALSE
  '',         // FALSE
  NULL        // FALSE
};

for (String s : slist) system.debug(
    StringUtilities.isNumeric(s)
);

I wrote some code that does some simple checks for data conversion as part of my XmlToJson example code that I posted last year. It's simply a Pattern that we cache, and use it to quickly determine if a string is any sort of number. Here's the relevant code. First, we store a couple of patterns to pick out Booleans, decimals, dates, and date-times (everything else in the demo is treated as text):

static Pattern 
    boolPat = Pattern.compile('^(true|false)$'),  decPat = Pattern.compile('^[-+]?\\d+(\\.\\d+)?$'), 
    datePat = Pattern.compile('^\\d{4}.\\d{2}.\\d{2}$'), 
    timePat = Pattern.compile('^\\d{4}.\\d{2}.\\d{2} (\\d{2}:\\d{2}:\\d{2} ([-+]\\d{2}:\\d{2})?)?$');

Then, when we want to auto-detect our data type, we use the following cascading ternary:

        Object value = 
            //  Nothing
            String.isBlank(nodeText)? null:
        //  Try boolean
        boolPat.matcher(nodeText).find()? 
            (Object)Boolean.valueOf(nodeText):
        //  Try decimals
        decPat.matcher(nodeText).find()?
            (Object)Decimal.valueOf(nodeText):
        //  Try dates
        datePat.matcher(nodeText).find()?
            (Object)Date.valueOf(nodeText):
        //  Try times
        timePat.matcher(nodeText).find()? 
            (Object)DateTime.valueOf(nodeText):
        //  Give up, use plain text
        (Object)nodeText;

You'll note that this code is try-catch free--it runs in just a few milliseconds in any scenario, which is deal for processing large amounts of values.

So, to make this work for you, you'd do this:

static Pattern decimalPattern = Pattern.compile('^[-+]?\\d+(\\.\\d*)?$');
public static Decimal getNumber(String value) {
    return String.isBlank(value) || !decimalPattern.matcher(value).find()?
        null: Decimal.valueOf(value);
}

So, if efficiency isn't your goal and you want to just write something that utilizes the current string functionality, something like this should work:

public Boolean isNumericOrDecimal(String s){
    if(s == null) return false;
    else if(s.contains('.') && s.indexOf('.') == s.lastIndexOf('.'))
        s = s.replace('.','');
    return s.isNumeric();
}

It would simply replace the decimal if and only if there is a decimal AND the decimal is in the same place (single decimal). Then it would return whether the remaining value was numeric or not.

Simple/quick test cases I tried:

'.123.' = false
'.123' = true
'123.' = true
'123' = true
'a123' = false

If you wanted the number to be correctly formatted you could write logic to say if the decimal was in the first or last position it would also return false, but this makes the assumption that there may be an inferred leading/trailing 0.