Is this a number?

17

2

Prelude:

I wanted to train making test cases, so I'm gonna try them on something easy.

Challenge:

Take any given (string) input (within visible ASCII range) and deduce if it's a number and output something that could be used to evaluate.

Rules:

  • A number will only contain the characters -0123456789,.
  • Answers are only required to recognise numbers between -1000000000 and 1000000000 (exclusive), but may recognise arbitrarily large numbers.
  • You can write a full program or a function.
  • If a number, return anything that could be used to discern it and document the output in the description (ex. My program outputs T if a number, F if not.).
  • Input will be any amount of characters within ASCII range or empty (if empty return whatever you'd output if not a number).
  • Numbers could include a decimal point (ex. 3.14). If they do, they must have at least one digit before the decimal point and at least one after it.
  • Numbers could have leading or trailing zeros (ex. 000001.00000).
  • The integer part of a number could be divided for readability into chunks of three digits with commas (ex. 1,000.23456). In this case they must be divided every three digits from right to left (ex. 1,234,567, 10,000.202, 123,234.00, 0,123.293).
  • Negatives numbers are indicated by a leading - (ex. -1.23). A leading + to indicate a positive number is not permitted, and should result in the falsy output.
  • Exceptions are NOT counted as valid and discernible output (except when they can pass outputs to standard out stream (ex. Exception on line N [...] can be put in as output for a number/nonnumber if the string is put to standard out stream.

Test cases:

(assuming the My program outputs T if a number, F if not. version)

123 -> T [on integer]
-123 -> T [negative numbers need to be handled]
0 -> T [on zero]
123.456 -> T [on floating point]
123,456.789 -> T [on evenly divided by 3 digits with comas]
123456789 -> T [thousand separators are not required]
0000001.00000 -> T [on leading/trailing zeros]
00.00 -> T [on zero and leading/trailing zeros]
999999999.9999999999999999999999999999999999999999999999999999 -> T [on close to the maximum value]
-999999999.9999999999999999999999999999999999999999999999999999 -> T [on close to the minimum value]
 -> F [on empty]
lolz -> F [on non-number value]
n4melyh4xor -> F [on non-number with digits]
  1.2 -> F [on space-padded]
9.3 1.3 -> F [on anyhow separated multiple numbers]
1e5 -> F [no scientific notation]
50cl05e.buty3ts0f4r -> F [on input with letters obscuring the number]
1,2,3,4.5678 -> F [on badly readability-divided number]
1,234.5,678 -> F [on readability-divided floating point part]
.234 -> F [on no leading zero]
+1 -> F [no leading + sign]
1.234.3 -> F [only one decimal point]
12345,678 -> F [on separator stopping mid-way]

, least characters is the winner.

n4melyh4xor

Posted 2016-12-17T08:20:11.280

Reputation: 507

Can we assume the input is always ASCII? – Brad Gilbert b2gills – 2016-12-17T17:04:54.160

@BradGilbertb2gills, yes. – n4melyh4xor – 2016-12-17T17:08:12.063

If -123 is OK, what about +456 --> good or bad. Or is + left out of the party? – chux - Reinstate Monica – 2016-12-17T17:39:22.467

@chux, the party train left before the + got there.
Better luck next time, +.
– n4melyh4xor – 2016-12-17T17:42:55.700

If the input is outside the range -1000000000 to 1000000000, should it return T or F? – Mitchell Spector – 2016-12-17T18:46:33.440

@MitchellSpector, if you are able to parse numbers bigger than that, feel free to. – n4melyh4xor – 2016-12-17T18:47:52.647

2When people ask for clarifications in comments, you should edit the question. It should never be necessary to read the comments in order to know the spec. I've edited to include the answers and some test cases. – Peter Taylor – 2016-12-17T23:04:39.237

"If a number, return anything that could be used to discern it and document the output in the description" - that's rather vague / poorly worded. If I output the input, it can still be used to discern whether the input was a number. – John Dvorak – 2016-12-18T15:51:49.387

A good test case would be one where the thousands separator stops mid-way. 12345,678 -> F – John Dvorak – 2016-12-18T15:56:14.870

@JanDvorak, cool, included. – n4melyh4xor – 2016-12-18T17:03:19.997

True ASCII is 7-bit, may I use PETSCII which is 8-bit ASCII-compatible character encoding ? – Shaun Bebbers – 2017-04-06T07:20:48.507

Answers

10

Retina, 28 29 31 40 34 bytes

^-?\d{1,3}((,\d{3})*|\d*)(\.\d+)?$

Outputs 1 if truthy, 0 otherwise. As far as I understand, in this case Retina outputs the number of matches the regex has on the input.

Try it online!

Test Suite

Edits

  • Fixed to comply with the "there should be digits after the decimal place" rule
  • Fixed to comply with the "handle negatives" rules
  • Fixed to comply with the optional comma separators
  • Fixed bug with thousands separators as noted by @MartinEnder
  • Golfed by removing conditional lookahead

RegExplanation

^-?\d{1,3}((,\d{3})*|\d*)(\.\d+)?$
^                                $  This marks the start and end of regex
 -?                                 Optional hyphen for negative number
   \d{1,3}                          Matches between 1 and 3 digits
          (             )           Capturing group
           (,\d{3})*                 Matches at least zero of comma and three numbers (in the case of separators)
                    |                OR
                     \d*             Matches at least zero digits (in the case of no separators)
                        (\.\d+)?    Matches an optional . and at least one digit (for decimal numbers)

user41805

Posted 2016-12-17T08:20:11.280

Reputation: 16 320

@MartinEnder Fixed! Also, how do you specify the flags (say flags for m and g) in Retina? – user41805 – 2016-12-17T15:05:42.723

Put a \`` in front of the regex, and then the modifiers go in front of that, e.g.m`^.$.g` does not exist in .NET, matches are global by default. – Martin Ender – 2016-12-17T15:14:49.197

3

JavaScript, 46 49 bytes

This is a direct port of my Retina answer. The only reason I used JS is so that there is an easy way to test the regex using the Snack Snippet below

f=s=>/^-?\d{1,3}((,\d{3})*|\d*)(\.\d+)?$/.test(s)

f=s=>/^-?\d{1,3}((,\d{3})*|\d*)(\.\d+)?$/.test(s)
<input oninput=p.innerText=f(value)><p id=p></p>

user41805

Posted 2016-12-17T08:20:11.280

Reputation: 16 320

If it weren't for some of the rules (such as the comma rule), one could simply do isNaN(+prompt()) for 16 characters. Such is life, I suppose – Matheus Avellar – 2017-04-06T01:25:21.973

2

Perl 6, 42 bytes

{?/^\-?[\d**1..3[\,\d**3]+|\d+][\.\d+]?$/}

Try it

Expanded:

{  # bare block lambda with implicit parameter 「$_」

  ? # turn the following into a Bool result

  /  # match implicitly against 「$_」

    ^                 # beginning of string

    \- ?              # optional leading 「-」

    [

      \d ** 1..3      # 1 to 3 digits
      [
        \,            # comma
        \d ** 3       # three digits
      ]+              # repeat at least once

    |                 # or

      \d+             # at least one digit

    ]

    [ \. \d+ ]?       # 「.」 followed by at least one digit optionally

    $  # end of string

  /
}

Brad Gilbert b2gills

Posted 2016-12-17T08:20:11.280

Reputation: 12 713

1

PHP, 62 bytes

<?=preg_match("#^-?(\d+|\d{1,3}(,\d{3})*)(\.\d+)?$",$argv[1]);

The builtin cannot handle commas and it accepts scientific notation; so I had to walk the regex way. <?=is_numeric($argv[1]); would have been 24 bytes.

Titus

Posted 2016-12-17T08:20:11.280

Reputation: 13 814

0

bash/Unix tools, 64 bytes

egrep -c '^-?([0-9]+|[0-9]{1,3}(,[0-9]{3})+)(\.[0-9]+)?$'<<<"$1"

Save this as a file, and pass the string to be tested as the first argument to the command.

Output is 0 for a non-number, 1 for a number.

If you're willing to accept input from stdin and if you can guarantee that the input consists of just one line, then you can remove the <<<"$1" at the end, resulting in 57 bytes.

As for the code itself, the -c option to egrep counts the number of matching lines (which will be either 0 or 1).

Mitchell Spector

Posted 2016-12-17T08:20:11.280

Reputation: 3 392

0

Pyth, 25 characters

:zCiCM"૧祥쪤"^T6

Compresses Kritixi Lithos’ regex.

Try it online. Uses Unicode characters outside the Basic Multilingual Plane, which TIO apparently can’t handle? This little app reports the wrong size for the string, though. This character/byte counter gets it right.

Lynn

Posted 2016-12-17T08:20:11.280

Reputation: 55 648

0

C89, 195 bytes

b,d,c,i;f(char*s){b=*s;for(d=c=i=0;i++,*(s++),d+=*s=='.',c+=*s==',',b=c?i&(i%4!=3)&*s==','?0:b:b;)if(*s==45&!i);else if((*s==32)|(*s==46)|*s==43|!isdigit(*s)&*s!=46&*s!=44)||!(d-1))b=0;return b;}

Ungolfed:

bool is_base10_number (const char* const str) {

  if(!str[0])return false;

  size_t
    dpts = 0,
    cmas = 0;

  for (size_t i = 0; i < strlen(str); i++) {

    if ((str[i] == '-') && (!i)){
      continue;
    }

    if (
      (str[i] == ' ')
      || (str[0] == '.')
      || (str[0] == '+')
      ||
      (
        (!isdigit(str[i]))
        && (str[i] != '.')
        && (str[i] != ',')
      )
    ) {
      return false;
    }

    dpts += str[i] == '.';
    cmas += str[i] == ',';

    if (cmas) {
      if (i & ((i % 4) != 3) & str[i] == ',') {
        return false;
      }
    }

  }

  if (dpts > 1) { return false; }

  return true;
}

cat

Posted 2016-12-17T08:20:11.280

Reputation: 4 989

0

Python 2, 79 bytes

Regex solution

import re
lambda s:[s]==re.findall('-?(?:\d{1,3}(?:,\d{3}\d+)(?:\.?\d+)?',s)


-?                                          | Starts with 0-1 dashes
  (?:\d{1,3}(?:,\d{3})+                     | Have 1-3 digits and 1+ (comma followed by 3 digits)
                       |\d+)                | or 1+ digits
                            (?:\.?\d+)?     | Ends with dot and 1+ digits or nothing

Try it online

Dead Possum

Posted 2016-12-17T08:20:11.280

Reputation: 3 256

0

c#, 75 bytes

bool t(string n){double v;return double.TryParse(n.Replace(",",""),out v);}

Johan du Toit

Posted 2016-12-17T08:20:11.280

Reputation: 1 524