2303 votos

Validar números decimales en JavaScript - IsNumeric()

¿Cuál es la forma más limpia y eficaz de validar números decimales en JavaScript?

Puntos de bonus para:

  1. Claridad. La solución debe ser limpia y simple.
  2. Multiplataforma.

Casos de prueba:

 01. IsNumeric('-1') => true
 02. IsNumeric('-1.5') => true
 03. IsNumeric('0') => true
 04. IsNumeric('0.42') => true
 05. IsNumeric('.42') => true
 06. IsNumeric('99,999') => *false*
 07. IsNumeric('0x89f') => *false*
 08. IsNumeric('#abcdef')=> *false*
 09. IsNumeric('1.2.3') => *false*
 10. IsNumeric('') => *false*
 11. IsNumeric('blah') => *false*

2860voto

CMS Puntos 315406

@Joel respuesta está bastante cerca, pero fallará en los siguientes casos:

// Whitespace strings:
IsNumeric(' ') == true;
IsNumeric('\t\t') == true;
IsNumeric('\n\r') == true;

// Number literals:
IsNumeric(-1) == false;
IsNumeric(0) == false;
IsNumeric(1.1) == false;
IsNumeric(8e5) == false;

Hace algún tiempo tuve que implementar una función IsNumeric para averiguar si una variable contiene un valor numérico, sin importar su tipo, podría ser un String que contiene un valor numérico (tuve que considerar también la notación exponencial, etc.), un objeto Number, prácticamente cualquier cosa podía ser pasada a la función, no podía hacer ningún tipo de suposición, teniendo cuidado con la conversión de tipos (p. ej. +true == 1; , pero true no debería ser considerada como "numeric").

Creo que valga la pena compartir este conjunto de + de 30 tests unitarios realizados a numerosas implementaciones de función, y compartir también la que pasa todos mis pruebas:

function isNumber(n) {
  return !isNaN(parseFloat(n)) && isFinite(n);
}

Actualización : He aquí cómo lo hace ahora jQuery (Mediados de 2014) :

isNumeric: function( obj ) {
    return !jQuery.isArray( obj ) && (obj - parseFloat( obj ) + 1) >= 0;
}

P.S. isNaN & isFinite tienen un comportamiento confuso debido a la conversión forzosa al número. En ES6, Number.isNaN & Number.isFinite iba a solucionar estos problemas. Mantenga esto en mente cuando se usen.

329voto

Joel Coehoorn Puntos 190579

¡Arrrgh! No escuches las respuestas de la expresión regular. RegEx es asqueroso para esto, y no estoy hablando sólo de la actuación. Es tan fácil de hacer sutil, imposible de detectar errores con tu expresión regular.

Si no puedes usar isNaN() esto debería funcionar mucho mejor:

function IsNumeric(input)
{
    return (input - 0) == input && (''+input).replace(/^\s+|\s+$/g, "").length > 0;
}

Así es como funciona:

El (input - 0) La expresión obliga a JavaScript a hacer un tipo de coacción sobre su valor de entrada; primero debe ser interpretado como un número para la operación de sustracción. Si esa conversión a un número falla, la expresión resultará en NaN . Este numérico el resultado es entonces comparado con el valor original que pasaste. Dado que el lado izquierdo es ahora numérico, se vuelve a utilizar el tipo de coacción. Ahora que la entrada de ambos lados fue coaccionada al mismo tipo a partir del mismo valor original, se pensaría que deberían ser siempre los mismos (siempre verdaderos). Sin embargo, hay una regla especial que dice NaN nunca es igual a NaN y así un valor que no puede ser convertido en un número (y sólo los valores que no pueden ser convertidos en números) resultarán falsos.

La comprobación de la longitud es para un caso especial que involucra cuerdas vacías. También ten en cuenta que cae en tu prueba de 0x89f, pero eso es porque en muchos entornos es una buena manera de definir un número literal. Si quieres captar ese escenario específico podrías añadir una comprobación adicional. Incluso mejor, si esa es tu razón para no usar isNaN() y luego sólo tienes que envolver tu propia función isNaN() que también puede hacer la comprobación adicional.

En resumen, si quieres saber si un valor puede ser convertido en un número, trata de convertirlo en un número.


Volví e investigué un poco para por qué una cadena de espacio blanco no tuvo el resultado esperado, y creo que ahora lo entiendo: una cadena vacía es coaccionada a 0 en lugar de NaN . Simplemente recortando la cadena antes de la comprobación de la longitud se encargará de este caso. Note que opté por un recorte basado en regex en lugar de la función .trim() para soportar IE8 (y sí, soy consciente de la ironía de comenzar mi respuesta disolviendo regex y luego usando uno en el código, aunque sólo de manera limitada). Por esta época el año que viene, cuando Windows XP ya no esté soportado, volveré y lo actualizaré para usar la función .trim() (y eliminaré estas dos frases... si lo recuerdo).

Ejecutar las pruebas de la unidad contra el nuevo código y sólo falla en los literales infinito y booleano, y la única vez que eso debería ser un problema es si estás generando código (en realidad, ¿quién escribiría un literal y comprobaría si es numérico? Deberías conozca ), y eso sería un código extraño para generar.

Pero, de nuevo, la única razón para usar esto es si por alguna razón tienes que evitar el isNaN().

65voto

Michael Haren Puntos 42641

Este camino parece funcionar bien:

function IsNumeric(input){
    var RE = /^-{0,1}\d*\.{0,1}\d+$/;
    return (RE.test(input));
}

Y para probarlo:

// alert(TestIsNumeric());

function TestIsNumeric(){
    var results = ''
    results += (IsNumeric('-1')?"Pass":"Fail") + ": IsNumeric('-1') => true\n";
    results += (IsNumeric('-1.5')?"Pass":"Fail") + ": IsNumeric('-1.5') => true\n";
    results += (IsNumeric('0')?"Pass":"Fail") + ": IsNumeric('0') => true\n";
    results += (IsNumeric('0.42')?"Pass":"Fail") + ": IsNumeric('0.42') => true\n";
    results += (IsNumeric('.42')?"Pass":"Fail") + ": IsNumeric('.42') => true\n";
    results += (!IsNumeric('99,999')?"Pass":"Fail") + ": IsNumeric('99,999') => false\n";
    results += (!IsNumeric('0x89f')?"Pass":"Fail") + ": IsNumeric('0x89f') => false\n";
    results += (!IsNumeric('#abcdef')?"Pass":"Fail") + ": IsNumeric('#abcdef') => false\n";
    results += (!IsNumeric('1.2.3')?"Pass":"Fail") + ": IsNumeric('1.2.3') => false\n";
    results += (!IsNumeric('')?"Pass":"Fail") + ": IsNumeric('') => false\n";
    results += (!IsNumeric('blah')?"Pass":"Fail") + ": IsNumeric('blah') => false\n";

    return results;
}

Tomé prestado ese regex de http://www.codetoad.com/javascript/isnumeric.asp . Explicación:

/^ match beginning of string
-{0,1} optional negative sign
\d* optional digits
\.{0,1} optional decimal point
\d+ at least one digit
$/ match end of string

50voto

camomileCase Puntos 884

UI de Yahoo! usa esto:

isNumber: function(o) {
    return typeof o === 'number' && isFinite(o);
}

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X