La gran calculadora enloquecida

La gran calculadora enloquecida

En este artículo voy a discutir un problema en el que pocas personas piensan. La simulación por computadora de varios procesos se vuelve cada vez más generalizada. Esta tecnología es maravillosa porque nos permite ahorrar tiempo y materiales que de otro modo se gastarían en experimentos químicos, biológicos, físicos y de otro tipo sin sentido. Un modelo de simulación por computadora del flujo de una sección del ala puede ayudar a reducir significativamente la cantidad de prototipos que se probarán en un túnel de viento real. Los experimentos numéricos tienen cada vez más confianza en la actualidad. Sin embargo, deslumbrados por el triunfo de la simulación por computadora, nadie se da cuenta del problema del crecimiento de la complejidad del software que hay detrás. La gente trata la computadora y los programas de computadora como un medio para obtener los resultados necesarios. Me preocupa que muy pocos sepan y se preocupen por el hecho de que el crecimiento del tamaño del software conduce a un crecimiento no lineal de la cantidad de errores de software. Es peligroso explotar una computadora tratándola como una gran calculadora. Entonces, eso es lo que pienso:necesito compartir esta idea con otras personas.

La gran calculadora

Al principio tenía la intención de titular este artículo algo así como "Si los programadores no pueden crear medicamentos, ¿por qué los médicos pueden crear programas?" Tome un programador imaginario:no se le permite desarrollar y preparar medicamentos. La razón es obvia:no tiene la educación necesaria para eso. Sin embargo, no es tan simple con la programación. Puede parecer que un médico imaginario que ha aprendido a programar será por defecto un programador exitoso y útil, especialmente dado que una habilidad de programación más o menos aceptable es mucho más fácil de dominar que la química orgánica y los principios de preparación de medicamentos.

Aquí yace una trampa. Un experimento informático requiere tanto cuidado como uno real. A los trabajadores de laboratorio se les enseña a lavar los tubos de ensayo después de los experimentos y asegurarse de que estén esterilizados. Pero pocos se preocupan realmente por el problema de que alguna matriz accidentalmente quede sin inicializar.

Los programadores son muy conscientes de que cuanto más complejo es el software, más complicados y sutiles son los errores que se producen en él. En otras palabras, estoy hablando de un crecimiento no lineal de la cantidad de errores que acompañan al crecimiento del tamaño del código. Los programas que realizan cálculos químicos o cualquier otro cálculo científico están lejos de ser simples, ¿no es así? Aquí es donde está el peligro. Está bien que un programador médico cometa errores. Cualquier programador, por hábil que sea, los hace de vez en cuando. Lo que no está bien, la gente tiende a confiar cada vez más en estos resultados. Calculas algo y sigues con tu negocio.

Quienes se dedican a la programación como actividad profesional saben lo peligroso que es este enfoque. Saben qué es un comportamiento indefinido y cómo un programa solo puede pretender que funciona bien. Hay numerosos artículos y libros que explican cómo desarrollar correctamente las pruebas unitarias y garantizar la exactitud de los cálculos.

Así es el mundo de los programadores. Pero me temo que el mundo de los químicos/físicos/médicos no es así. Nunca escriben un programa complejo, simplemente no piensan de esa manera. Usan la computadora como si fuera una gran calculadora. Esta comparación fue sugerida por uno de nuestros lectores. Permítanme citar su comentario completo aquí, para que los lectores de habla inglesa también puedan aprender sobre él, una vez que se traduzca el artículo.

yo puede decir algo activado esto asunto de mi propio experiencia. Aunque ser un profesional programador, yo en realidad ven de un familia de físicos y tener física educación. En el momento cuándo yo tenía a elegir que universidad a entrar, el llamar de sangre era más fuerte que mi fe en el brillante futuro de TI. Entonces, yo entró un física universidad, más bien prestigioso activado el locales escala, que en hecho es un "jardín de infantes" supervisado por un grande investigación instituto en mi nativo ciudad Nizhni Nóvgorod. Personas quien saber el asunto voluntad en una vez adivina que investigación instituto y que universidad yo malo.

Mientras estudiando ahí, yo bastante naturalmente probado a ser uno de el mejor en programación (y matemático métodos de físico modelado en particular). eso era en el igual tiempo yo también supuse fuera el siguiendo cosas:

1. Físicos tender para ver el ordenador como un grande multifuncional calculadora permitir a dibujar un gráfico de Eta contra Theta con Gama yendo a infinito. Como uno puede naturalmente esperar, ellos son principalmente interesado en el gráfico en sí mismo, no el programa.

2. Como un consecuencia de el primero hecho, un programador es no visto como un profesión. A programador es solo el chico quien sabe cómo a usar el Grande Calculadora a dibujar el necesario gráfico. Ellos no cuidado que camino eso voluntad ser hecho - en todos. Lo siento, que hizo decir? Estático análisis? Versión controlar? Ah, ven en, chicos! C++ es el idioma de programadores; físicos escribir en ¡FORTRAN!

3. Como un consecuencia de el anterior hecho, cualquiera quien es yendo a dedicar su vida a escribir programas a hacer físico modelado, incluso multiuso unos, incluso duro como infierno unos, es pero un apéndice a el Grande Calculadora. Él es no incluso un persona - solo un amable de... Por el camino, eso era no solo yo tratado en tal un camino por físicos (yo era solo un ordinario estudiante, después todos) - pero incluso el mejor ordenador modelado especialista en el investigación instituto quien enseñado un computacional métodos curso en nuestra universidad y quien, cuándo yo convertido a él como mi tesis asesor mientras escribir mi término papel, dijo a yo casi sencillo, "Ellos voluntad despreciar tú, entonces ser preparado a tolerar eso".

yo no quiero a tolerar eso y después graduación izquierda el ordenador modelado área para el campo dónde programadores son no pensamiento a ser untermenschen. yo esperanza esto ejemplo voluntad ayuda entender por qué iniciativas me gusta introduciendo estático análisis incluso en proyectos relativamente grandes (alrededor de 20 o 30 desarrolladores) sobre modelado por computadora es un trabajo inútil. Simplemente puede que no haya una persona que sepa lo que es. Y si una persona así está en el equipo, lo más probable es que lo pisoteen porque no necesitan ningún programador moderno de tu parte. "Hemos estado prescindiendo de ellos durante cien años, y haremos más".

Aquí hay otra historia para aquellos que aún no están aburridos. Mi padre, aunque es jubilado, todavía trabaja en una empresa de ingeniería de defensa muy grande aquí, en Nyzhny Novgorod (es la más grande de nuestra ciudad y una de las más grandes de todo el país; nuevamente, aquellos que conocen el tema lo adivinarán; ) ). Lleva toda la vida programando en FORTRAN. Empezó en la época en que se utilizaban las tarjetas perforadas. No lo culpo por no estudiar C++. Ya era demasiado tarde para él hace 10 años, y todavía le va bastante bien. Sin embargo, existen ciertas precauciones de seguridad en esta empresa, 2/3 del personal del cual se dedica a programar de una forma u otra:

1. Sin internet. En absoluto. Necesitas literatura, vas a la biblioteca. ¿Desbordamiento de pila? ¿Que es eso? Si necesita enviar un correo electrónico, debe enviar una solicitud por escrito al jefe explicando a quién y para qué desea enviarlo. Solo unos pocos elegidos pueden usar Internet "contra un recibo". Gracias a Dios, al menos tienen una red interna.

2. Sin derechos de administración en su computadora. Tal vez esta restricción tenga sentido para la masa de cuello blanco, pero no puedo imaginarme a un programador que se sienta satisfecho con ella.

3. (No se relaciona con el tema; solo una ilustración). Ni siquiera puedes traer un teléfono celular con una cámara integrada (¿has visto algunos sin ella hoy en día?).

Como resultado, incluso los empleados jóvenes escriben código en FORTRAN, mientras que los programadores realmente capacitados son muy pocos. Lo sé con certeza porque entrené a un chico de 25 años a quien mi padre me había recomendado como programador prometedor.

Aquí está mi veredicto:están atrapados en los años 80 allí. Incluso teniendo en cuenta que tienen salarios bastante buenos, no iría allí por nada del mundo.

Estos son solo dos ejemplos de la vida de la élite intelectual. No pretendo desacreditar a nadie:hacen su trabajo lo suficientemente bien, pero mi corazón está sangrando mientras observo los molinos de viento con los que mi padre tiene que luchar a veces. (Gracias a Dios, logré persuadirlo para que comenzara a usar git recientemente). Sin programación orientada a objetos en un proyecto de un millón de líneas, sin análisis estático, nada.

Simplemente tiene que ver con el rasgo humano de ser muy conservador en áreas que no son sus puntos fuertes.

Ilja Mayzus. El comentario original.

El núcleo de esta historia es la ideología de tratar a la computadora como una gran calculadora. En ese caso, no necesitas saber más sobre él de lo que se merece su hermano menor, la calculadora de bolsillo. Y es la forma en que se utiliza en muchas áreas. Hagamos una digresión por un momento y echemos un vistazo al mundo de la física. Veamos cómo otra teoría encuentra una confirmación. Para hacer esto, nuevamente tendré que citar un gran extracto del libro de Bryan Greene "El universo elegante:supercuerdas, dimensiones ocultas y la búsqueda de la teoría definitiva" [1]:

Nosotros todos acurrucados alrededor Morrison ordenador en el oficina él y yo compartido. Aspinwall dicho Morrison cómo a traer su programa arriba activado el pantalla y mostrado nosotros el preciso formulario para el obligatorio entrada. Morrison adecuadamente formateado el resultados nosotros tenía generado el anterior noche, y nosotros fueron establecer a ir.

El particular cálculo nosotros fueron actuando importes, aproximadamente hablando, a determinar el masa de un cierto partícula especie—a específico vibratorio patrón de un cadena:cuando en movimiento a través un universo cuyo Calabi-Yau componente nosotros tenía gastado todos caída identificando. Nosotros esperado, en línea con el estrategia discutido antes, eso esto masa sería de acuerdo idénticamente con un parecido cálculo hecho activado el Calabi-Yau forma emergente de el desgarro del espacio fracaso transición. El último era el relativamente fácil cálculo, y nosotros tenía completado eso semanas antes; el responder convertido fuera a ser 3, en el particular unidades nosotros fueron usando. Desde nosotros fueron ahora haciendo el supuesto espejo cálculo numéricamente activado un ordenador, nosotros esperado a obtener algo extremadamente cerrar a pero no exactamente 3, algo me gusta 3.000001 o 2.999999, con el pequeño diferencia surgiendo de redondeo errores.

Morrison sábado en el ordenador con su dedo flotando cambio el ingresar botón. Con el tensión montaje él dijo, "Aquí va," y establecer el cálculo en movimiento. En un pareja de segundos el ordenador devuelto es respuesta: 8.999999. Mi corazón se hundió. Podría eso ser eso desgarro del espacio fracaso transiciones romper el espejo relación, probable indicando eso ellos no puedo en realidad ocurre? Casi inmediatamente, aunque, nosotros todos realizado eso algo gracioso debe ser yendo encendido. Si allí era un real desajuste en el física siguiendo de el dos formas, eso era extremadamente poco probable eso el ordenador cálculo debería rendimiento un responder entonces cerrar a un entero número. Si nuestro ideas fueron incorrecto, allí era no motivo en el mundo a esperar cualquier cosa pero un al azar colección de dígitos. Nosotros tenía obtenido un incorrecto respuesta, pero uno eso sugerido, quizás, eso nosotros tenía solo hecho algunos sencillo aritmética error. Aspinwall y yo fue a el pizarra, y en un momento nosotros encontrado nuestra error: nosotros tenía cayó un factor de 3 en el "más simple" cálculo nosotros tenía hecho semanas antes; el verdadero resultado fue 9. Por lo tanto, la respuesta de la computadora fue justo lo que queríamos .

De curso, el después del hecho acuerdo era solo marginalmente convincente. Cuando saber el responder quiero, eso es a menudo todos también fácil a figura fuera un camino de conseguir eso. Nosotros necesario a hacer otro ejemplo. Tener ya escrito todos de el necesario ordenador código, esto era no duro a hacer. Nosotros calculado otro partícula masa activado el superior Calabi-Yau forma, ser cuidado esto tiempo a hacer no errores. Nosotros encontrado el respuesta: 12. Una vez otra vez, nosotros acurrucados alrededor el ordenador y establecer eso activado es camino. Segundos más tarde eso devuelto 11.999999. Acuerdo. Nosotros tenía mostrado eso el supuesto espejo es el espejo, y por lo tanto desgarro del espacio fracaso transiciones son parte de el física de cadena teoría.

En esto yo saltó fuera de mi silla y corrió un sin restricciones victoria vuelta alrededor el oficina. Morrison transmitido de detrás el ordenador. Aspinwall's reacción, aunque, era más bien diferente. "Eso es genial, pero yo sabía eso sería trabajo," él con calma dijo. "Y dónde está mi ¿cerveza?

Realmente creo que son unos genios. Pero imaginemos por un momento que fueran algunos estudiantes comunes quienes usaron este enfoque para calcular una integral. No creo que los programadores lo tomen en serio entonces. ¿Y si el programa generara 3 de inmediato? ¿El error sería tomado como prueba final? Creo que se aclararía más tarde, durante una nueva revisión por parte de ellos mismos o de sus colegas científicos. Aún así, el "programador esférico ideal en el vacío" está muerto de miedo por este hecho.

Así son las cosas en la realidad. No son solo las computadoras personales las que se usan de esa manera, sino también los sistemas de clústeres que se explotan para los cálculos científicos. Y lo que es más aterrador, la gente confía en los resultados que producen los programas. En el futuro, nos ocuparemos de más cálculos de este tipo, y el precio de tener errores de software también será más alto.

¿No es hora de cambiar algo?

Sí, nadie me puede prohibir que me pegue una tirita en un corte; Supongo que puedo recomendarte algún medicamento para tomar cuando te hayas resfriado. Pero no más que eso. No puedo perforar un diente ni escribir una receta.

¿No le parece razonable que los desarrolladores que crean un sistema de software cuya responsabilidad se extiende más allá de cierto alcance también deban confirmar su habilidad?

Sé que existen varias certificaciones. Pero estoy hablando de una cosa diferente ahora. La certificación está destinada a garantizar que el código del programa se ajuste a ciertos estándares. Previene parcialmente el desbaste, de forma indirecta. Pero la gama de áreas donde la certificación es un requisito estricto es bastante limitada. Obviamente, no cubre todo el conjunto de áreas y situaciones en las que el uso descuidado de la Gran Calculadora puede causar mucho daño.

Ejemplo del Peligro

Supongo que muchos de ustedes encuentran mis preocupaciones demasiado abstractas. Es por eso que sugiero examinar algunos ejemplos de la vida real. Existe el paquete de código abierto Trans-Proteomic Pipeline (TPP) diseñado para resolver varias tareas en biología. Sin duda, es utilizado por sus desarrolladores y quizás por algunas organizaciones de terceros. Creo que cualquier error en él ya es un problema potencial. y tiene bugs? Sí, lo hace; y aún más están apareciendo. Verificamos este proyecto hace un año y lo informamos en la publicación del blog "Análisis del proyecto Trans-Proteomic Pipeline (TPP)".

Que ha cambiado desde entonces? Nada. El proyecto continúa desarrollando y acumulando nuevos errores. La ideología de la Gran Calculadora ha ganado. Los desarrolladores no están escribiendo un proyecto de alta calidad con la mínima cantidad de errores posibles. Simplemente resuelven sus tareas; de lo contrario, habrían reaccionado de alguna manera al artículo del año pasado y considerado introducir algunas herramientas de análisis estático. No quiero decir que necesariamente deban elegir PVS-Studio; hay muchos otros analizadores de código estático. El punto es que su aplicación responsable continúa recolectando la mayoría de los errores triviales. Veamos qué nuevos tienen.

1. Algún chapucero sigue escribiendo bucles incorrectos

En el artículo anterior mencioné condiciones de bucle incorrectas. La nueva versión del paquete también los tiene.

double SpectraSTPeakList::calcDot(SpectraSTPeakList* other) {
  ....
  for (i = this->m_bins->begin(), j = other->m_bins->begin(); 
       i != this->m_bins->end(), j != other->m_bins->end();
       i++, j++) {
    d = (*i) * (*j);
    dot += d; 
  }
  ....
}

Mensaje de diagnóstico de PVS-Studio:V521 Tales expresiones que usan el operador ',' son peligrosas. Asegúrate de que la expresión sea correcta. spectrastpeaklist.cpp 504

En la verificación "i !=this->m_bins->end(), j !=other->m_bins->end()", la expresión antes de la coma no verifica nada. El operador ',' se usa para ejecutar expresiones tanto a la derecha como a la izquierda en el orden de izquierda a derecha y devolver el valor de la expresión correcta . Así es como debería verse el cheque correcto:

i != this->m_bins->end() && j != other->m_bins->end()

El mismo defecto también se puede encontrar en los siguientes fragmentos:

  • spectrastpeaklist.cpp 516
  • spectrastpeaklist.cpp 529
  • spectrastpeaklist.cpp 592
  • spectrastpeaklist.cpp 608
  • spectrastpeaklist.cpp 625
  • spectrastpeaklist.cpp 696

2. Eliminación de referencias de puntero nulo

Este error no generará resultados de cálculo incorrectos; en su lugar, provocará un bloqueo, lo cual es mucho mejor. Sin embargo, sería extraño no mencionar estos errores.

void ASAPRatio_getDataStrctRatio(dataStrct *data, ....)
{
  ....
  int *outliers, *pepIndx=NULL;
  ....
  //pepIndx doesn't change
  ....
  if(data->dataCnts[i] == 1 && pepIndx[i] == 0)  
     data->dataCnts[i] = 0;
  ....
}

Mensaje de diagnóstico de PVS-Studio:V522 Es posible que se elimine la referencia del puntero nulo 'pepIndx'. asapcgidisplay2main.cxx 534

El mismo defecto también se puede encontrar en los siguientes fragmentos:

  • Puntero 'péptidos'. asapcgidisplay2main.cxx 556
  • Puntero 'péptidos'. asapcgidisplay2main.cxx 557
  • Puntero 'péptidos'. asapcgidisplay2main.cxx 558
  • Puntero 'péptidos'. asapcgidisplay2main.cxx 559
  • Puntero 'péptidos'. asapcgidisplay2main.cxx 560
  • Puntero 'pepIndx'. asapcgidisplay2main.cxx 569

3. Matrices no borradas

static void clearTagNames() {
   std::vector<const char *>ptrs;
   for (tagname_set::iterator i = tagnames.begin();
        i!=tagnames.end();i++) {
      ptrs.push_back(*i);
   }
   for (tagname_set::iterator j = attrnames.begin();
        j!=attrnames.end();j++) {
      ptrs.push_back(*j);
   }
   tagnames.empty();
   attrnames.empty();
   for (size_t n=ptrs.size();n--;) {
      delete [] (char *)(ptrs[n]); // cast away const
   }
}

En este código, el analizador ha capturado dos matrices sin borrar a la vez:

V530 Se requiere utilizar el valor de retorno de la función 'vacío'. etiqueta.cxx 72

V530 Se requiere utilizar el valor de retorno de la función 'vacío'. etiqueta.cxx 73

Deberías llamar a la función clear() en lugar de a la función empty().

4. Objetos de clase no inicializados

class ExperimentCycleRecord {
public:
  ExperimentCycleRecord() {
    ExperimentCycleRecord(0,0,0,True,False);
  }
  ExperimentCycleRecord(long lExperiment, long lCycleStart,
                        long lCycleEnd, Boolean bSingleCycle,
                        Boolean bRangleCycle)
  {
    ....
  }
  ....
}

Mensaje de diagnóstico de PVS-Studio:V603 El objeto se creó pero no se está utilizando. Si desea llamar al constructor, debe usar 'this->ExperimentCycleRecord::ExperimentCycleRecord(....)'. mascotconverter.cxx 101

El constructor ExperimentCycleRecord() no hace lo que se supone que debe hacer; no inicializa nada. El desarrollador puede ser un excelente químico, pero si no sabe cómo usar el lenguaje C++ correctamente, sus cálculos con memoria no inicializada no valen la pena. Es como usar un tubo de ensayo sucio.

En lugar de llamar a otro constructor, la línea "ExperimentCycleRecord(0,0,0,True,False);" crea un objeto temporal que será destruido después de eso. Este patrón de error se analiza en detalle en el artículo "No vadees en aguas desconocidas. Primera parte".

El mismo defecto también se puede encontrar en los siguientes fragmentos:

  • asapratiopeptideparser.cxx 57
  • asapratiopeptidecgidisplayparser.cxx 36
  • cruxdiscrimfunction.cxx 36
  • discrimvalmixturedistr.cxx 34
  • mascotdiscrimfunction.cxx 47
  • mascotscoreparser.cxx 37
  • función de disco en tándem.cxx 35
  • tandemkscoredf.cxx 37
  • tandemnativedf.cxx 37

5. Comentarios que violan la lógica de ejecución

int main(int argc, char** argv) {
  ....
  if (getIsInteractiveMode())  
    //p->writePepSHTML();
  //p->printResult();

  // regression test?
  if (testType!=NO_TEST) {
     TagListComparator("InterProphetParser",testType,
       outfilename,testFileName);
  ....
}

Mensaje de diagnóstico de PVS-Studio:V628 Es posible que la línea no haya sido comentada correctamente, alterando así la lógica de funcionamiento del programa. interprophetmain.cxx 175

Después del operador 'si', se comentaron algunas líneas que ejecutan algunas operaciones. Como resultado, la lógica del programa cambió bastante diferente a lo esperado. El programador no quería que se hiciera ninguna acción después de ejecutar la condición. En cambio, el operador 'si' afecta el código a continuación. Como consecuencia, la salida de las pruebas ahora depende no solo de la condición "testType!=NO_TEST", sino también de la condición "getIsInteractiveMode()". Es decir, la prueba puede no probar nada. Es por eso que recomiendo enfáticamente no confiar completamente en una sola metodología de prueba (por ejemplo, TDD).

6. Errores de imprenta

Los errores tipográficos se encuentran en todas partes y todo el tiempo. No es tan malo si obtienes menos puntos de golpe después de una explosión en un juego de lo que deberías, debido a ese error. Pero, ¿qué significan los datos incorrectos al calcular las reacciones químicas?

void ASAPRatio_getProDataStrct(proDataStrct *data, char **pepBofFiles)
{
  ....
  if (data->indx == -1) {
    data->ratio[0] = -2.;
    data->ratio[0] = 0.;
    data->inv_ratio[0] = -2.;
    data->inv_ratio[1] = 0.;
    return;
  }
  ....
}

Mensaje de diagnóstico de PVS-Studio:V519 A la variable 'data->ratio[0]' se le asignan valores dos veces seguidas. Quizás esto sea un error. Comprobar líneas:130, 131. asapcgidisplay2main.cxx 131

A una misma variable se le asignan por error dos valores diferentes. El código correcto es este:

data->ratio[0] = -2.;
data->ratio[1] = 0.;

Este fragmento fue luego copiado y pegado en otras partes del programa:

  • asapcgidisplay2main.cxx 338
  • asapcgidisplay2main.cxx 465
  • asapratioproteincgidisplayparser.cxx 393
  • asapratioproteincgidisplayparser.cxx 518

7. Comparando valores firmados y sin firmar

La comparación adecuada de valores con y sin signo requiere cierta habilidad. Las calculadoras ordinarias no manejan valores sin signo, pero el lenguaje C++ sí.

size_type size() const;
void computeDegenWts()
{
  ....
  int have_cluster = 0;
  ....
  if ( have_cluster > 0 && ppw_ref.size() - have_cluster > 0 )
  ....
}

Mensaje de diagnóstico de PVS-Studio:V555 La expresión 'ppw_ref.size() - have_cluster> 0' funcionará como 'ppw_ref.size() !=have_cluster'. proteinprophet.cpp 6767

El programador quería que se ejecutara la comprobación "ppw_ref.size()> have_cluster". Pero obtuvo algo muy diferente en su lugar.

Para que quede más claro, supongamos que tenemos el tipo 'size_t' que es de 32 bits. Supongamos que la función "ppw_ref.size()" devuelve 10 mientras que la variable have_cluster es igual a 15. La función ppw_ref.size() devuelve el tipo sin signo 'size_t'. De acuerdo con las reglas de C++, el operando correcto en la operación de resta también debe tener el tipo 'size_t' antes de que se ejecute la resta. Está bien por ahora:tenemos 10u a la izquierda y 15u a la derecha.

Aquí va la resta:

10u - 15u

Y aquí es donde tenemos un problema. Esas mismas reglas de C++ nos dicen que el resultado de la resta entre dos variables sin signo también debe ser sin signo.

Significa que 10u - 15u =FFFFFFFBu. Como sabes, 4294967291 es mayor que 0.

The Big Calculator Riot tiene éxito. Escribir un algoritmo teórico correcto es solo la mitad del trabajo. También debe escribir un código correcto.

Un error similar se puede encontrar en el siguiente fragmento:

double SpectraSTPeakList::calcXCorr() {
  ....
  for (int tau = -75; tau <= 75; tau++) {
  
    float dot = 0.0;
    for (unsigned int b = 0; b < numBins; b++) {
      if (b + tau >= 0 && b + tau < (int)numBins) {
        dot += (*m_bins)[b] * theoBins[b + tau] / 10000.0;
      }
    }
    ....
  ....
}

Mensaje de diagnóstico de PVS-Studio:V547 La expresión 'b + tau>=0' siempre es verdadera. El valor de tipo sin firmar siempre es>=0. spectrastpeaklist.cpp 2058

Como puedes ver, la variable 'tau' toma valores dentro del rango [-75, 75]. Para evitar el desbordamiento de la matriz, se utiliza la verificación b + tau>=0. Supongo que ya entendiste que esta verificación no funcionará. La variable 'b' tiene el modificador 'sin firmar'. Significa que el resultado de la expresión "b + tau" tampoco está firmado. Y un valor sin signo siempre es mayor o igual a 0.

8. Bucle extraño

const char* ResidueMass::getStdModResidues(....) {
  ....
  for (rmap::const_iterator i = p.first; i != p.second; ++i) {
    const cResidue &r = (*i).second;
    if (r.m_masses[0].m_nterm) {
        n_term_aa_mod = true;
    } else if (r.m_masses[0].m_cterm) {
        c_term_aa_mod = true;
    }
    return r.m_residue.c_str();
  }

  if(! strcmp(mod, "+N-formyl-met (Protein)")) {
    return "n";
  } if (! strcmp(mod, "13C6-15N2 (K)")) {
    return "K";
  } if (! strcmp(mod, "13C6-15N4 (R)")) {
    return "R";
  ....  
}

Mensaje de diagnóstico de PVS-Studio:V612 Un 'retorno' incondicional dentro de un bucle. masaresiduo.cxx 1442

Existe el operador 'return' dentro del ciclo y se llama en cualquier caso. El ciclo puede ejecutarse solo una vez, después de lo cual la función termina. Es un error de imprenta aquí o falta alguna condición antes del operador 'return'.

9. Cálculos aproximados

double RTCalculator::getUsedForGradientRate() {
  if (rts_.size() > 0)
    return used_count_ / rts_.size();
  return 0.;
}

Mensaje de diagnóstico de PVS-Studio:V636 La expresión 'used_count_ / rts_.size()' se transformó implícitamente del tipo 'int' al tipo 'double'. Considere utilizar una conversión de tipos explícita para evitar la pérdida de una parte fraccionaria. Un ejemplo:doble A =(doble)(X) / Y;. rtcalculator.cxx 6406

Dado que la función devuelve valores del tipo doble, me parece razonable suponer lo siguiente.

Cuando a la variable 'used_count_' se le asigna el valor 5 y la función rts_.size() devuelve 7, el resultado aproximado es 0,714. Sin embargo, la función getUsedForGradientRate() devolverá 0 en este caso.

La variable 'used_count_' tiene el tipo 'int'. La función rts_.size() también devuelve un valor 'int'. Se produce una división de enteros y el resultado es obvio:es cero. Entonces cero se convierte implícitamente en doble, pero no importa en este punto.

Para corregir el defecto, el código debe reescribirse de la siguiente manera:

return static_cast<double>(used_count_) / rts_.size();

Otros defectos de este tipo:

  • cgi_pep3d_xml.cxx 3203
  • cgi_pep3d_xml.cxx 3204
  • asapratiopeptideparser.cxx 4108

10. Gran y poderoso Copiar y Pegar

La función setPepMaxProb() contiene algunos bloques grandes de aspecto similar. En este fragmento se puede sentir ese olor específico de la técnica Copiar-Pegar. Usarlo naturalmente resulta en un error. Tuve que abreviar SIGNIFICATIVAMENTE el texto de muestra. El error es muy notorio en el código abreviado, pero es casi imposible verlo en el código original. Sí, es un anuncio de herramientas de análisis estático en general y de PVS-Studio en particular.

void setPepMaxProb( bool use_nsp, bool use_fpkm, 
  bool use_joint_probs, bool compute_spectrum_cnts )
{  
  double prob = 0.0;
  double max2 = 0.0;
  double max3 = 0.0;
  double max4 = 0.0;
  double max5 = 0.0;
  double max6 = 0.0;
  double max7 = 0.0;
  ....
  if ( pep3 ) { ... if ( use_joint_probs && prob > max3 ) ... }
  ....
  if ( pep4 ) { ... if ( use_joint_probs && prob > max4 ) ... }
  ....
  if ( pep5 ) { ... if ( use_joint_probs && prob > max5 ) ... }
  ....
  if ( pep6 ) { ... if ( use_joint_probs && prob > max6 ) ... }
  ....
  if ( pep7 ) { ... if ( use_joint_probs && prob > max6 ) ... }
  
  ....
}

V525 El código que contiene la colección de bloques similares. Verifique los elementos 'max3', 'max4', 'max5', 'max6', 'max6' en las líneas 4664, 4690, 4716, 4743, 4770. proteinprophet.cpp 4664

Mensaje de diagnóstico de PVS-Studio:V525 El código que contiene la colección de bloques similares. Check items 'max3', 'max4', 'max5', 'max6', 'max6' in lines 4664, 4690, 4716, 4743, 4770. proteinprophet.cpp 4664

Unfortunately, the V525 diagnostic produces many false positives and therefore referred to the third-level warnings. But if one overcomes one's laziness and study this class of warnings, one may find numbers of such nice bugs.

11. Pointer is not initialized sometimes

int main(int argc, char** argv) {
  ....
  ramp_fileoffset_t *pScanIndex;
  ....
  if ( (pFI=rampOpenFile(mzXmlPath_.c_str()))==NULL) {
    ....
  } else {
    ....
    pScanIndex = readIndex(pFI, indexOffset, &iAnalysisLastScan);
    ....
  }
  ....
  if (pScanIndex != NULL)
    free(pScanIndex);

  return 0;
}

PVS-Studio's diagnostic message:V614 Potentially uninitialized pointer 'pScanIndex' used. sqt2xml.cxx 476

This program may crash at the end if the function rampOpenFile() returns NULL. It's not critical yet unpleasant.

Here's another variable that may remain uninitialized:

  • Potentially uninitialized pointer 'fp_' used. dta-xml.cpp 307

12. Virtual destructor missing

class DiscriminantFunction {
public:
  DiscriminantFunction(int charge);
  virtual Boolean isComputable(SearchResult* result) = 0;
  virtual double getDiscriminantScore(SearchResult* result) = 0;
  virtual void error(int charge);
protected:
  int charge_;
  double const_;
}; // class

class CometDiscrimFunction : public DiscriminantFunction;
class CruxDiscrimFunction : public DiscriminantFunction;
class InspectDiscrimFunction : public DiscriminantFunction;
.....

class DiscrimValMixtureDistr : public MixtureDistr {
  ....
  DiscriminantFunction* discrim_func_;
  ....
};

DiscrimValMixtureDistr::~DiscrimValMixtureDistr() {
  delete[] posinit_;
  delete[] neginit_;
  delete discrim_func_;
}

PVS-Studio's diagnostic message:V599 The virtual destructor is not present, although the 'DiscriminantFunction' class contains virtual functions. discrimvalmixturedistr.cxx 206

A number of classes are inherited from the DiscriminantFunction class. For example, such is the class DiscrimValMixtureDistr. Its destructor frees memory; therefore, it's very desirable that you call it. Unfortunately, the DiscriminantFunction class's destructor is not declared as a virtual one - with all the ensuing consequences.

13. Miscellaneous

There are numbers of small defects which won't have serious consequences but are still not very pleasant to have in your code. There are also strange fragments, but I can't say for sure if they are incorrect. Here's one of them:

Boolean MixtureModel::iterate(int counter) {
  ....
  if (done_[charge] < 0) {
    done_[charge];
  }
  else if (priors_[charge] > 0.0) {
    done_[charge] += extraitrs_;
  }
  ....
}

PVS-Studio's diagnostic message:V607 Ownerless expression 'done_[charge]'. mixturemodel.cxx 1558

¿Qué es? Incomplete code? Or maybe the programmer just wanted to point it out that nothing should be done if the "done_[charge] <0" condition is true?

And here you are an incorrect way of freeing memory. Any critical consequences are unlikely, but still the code smells.

string Field::getText(....)
{
  ....
  char* pepString = new char[peplen + 1];
  ....
  delete pepString;
  ....
}

PVS-Studio's diagnostic message:V611 The memory was allocated using 'new T[]' operator but was released using the 'delete' operator. Considere inspeccionar este código. It's probably better to use 'delete [] pepString;'. pepxfield.cxx 1023

The correct way of doing this is to write "delete [] pepString". There are many other defects of this kind:

  • cruxdiscrimvalmixturedistr.cxx 705
  • cruxdiscrimvalmixturedistr.cxx 715
  • mascotdiscrimvalmixturedistr.cxx 426
  • mascotdiscrimvalmixturedistr.cxx 550
  • mascotdiscrimvalmixturedistr.cxx 624
  • phenyxdiscrimvalmixturedistr.cxx 692
  • probiddiscrimvalmixturedistr.cxx 487
  • probiddiscrimvalmixturedistr.cxx 659
  • tandemdiscrimvalmixturedistr.cxx 731
  • tandemdiscrimvalmixturedistr.cxx 741

And here's an incorrect implementation of the "--" operator. It doesn't seem to be used anywhere, otherwise the bug would quickly reveal itself.

CharIndexedVectorIterator operator++(int)
{  // postincrement
  CharIndexedVectorIterator _Tmp = *this;
  ++m_itr;
  return (_Tmp);
}

CharIndexedVectorIterator& operator--()
{  // predecrement
  ++m_itr;
  return (*this);
}

PVS-Studio's diagnostic message:V524 It is odd that the body of '--' function is fully equivalent to the body of '++' function. charindexedvector.hpp 81

The operators "--" and "++" are implemented in the same way. They must have been copied-and-pasted then:

  • charindexedvector.hpp 87
  • charindexedvector.hpp 159
  • charindexedvector.hpp 165

Let's stop here. It all is not very interesting, and the article is big enough. As usual, I'm urging the developers not to limit themselves to fixing only the mentioned defects. Download and check the project with PVS-Studio yourself. I could have missed many errors. We can even grant you a free registration key for some time.

Resumen

Unfortunately, the article has appeared a bit tangled. What did the author want to say, after all? I'll try to repeat in a very brief form my ideas I want to share with you.

  • We are currently using more and more programs to perform scientific and engineering computations and simulate various processes, and we grow to trust them.
  • Programs get very complicated. Professional programmers understand it very well that one cannot approach the task of creating a software package for computer simulation in the same way as using a software calculator. The growth of software complexity leads to an exponential increase of the number of errors [2].
  • It appears that physicists/biologists/medics cannot simply calculate something in the usual manner. One cannot ignore the software complexity increase and the consequences of incorrect computations arising from imperfect knowledge of a programming language.
  • In this article I've given arguments to prove that this is the real state of things. The first quotation tells us that people tend to treat the computer as an ordinary calculator. The second quotation just reaffirms this idea. The error samples discussed after that are meant to demonstrate that people really make mistakes when treating computer simulation software in such a way. So, my anxiety has solid ground.

So, what shall we do?

First of all, I'd like you to realize this problem and tell your colleagues from related areas. It's been clear to programmers for a long time that the software complexity growth and silly mistakes in large projects may easily turn into a source of great harm. On the other hand, those people who treat programming and computers just as a tool don't know that and don't bother to think about it. So, we need to draw their attention to this problem.

Here you are an analogy. Imagine a man who has got him a cudgel and starts hunting some animals. The cudgel in his hands gradually turns into a stone axe, then a sword, and finally a gun. But he still uses it just to stun hares by hitting them on the head. It's not only that this way of using the weapon is absolutely inefficient; it has also become much more dangerous now (he can accidentally shoot himself or his fellow men). Hunters from the "programmers" tribe quickly adapt themselves to these changes. The rest don't have time for that - they are busy hunting hares. After all, it's all about the hares. We need to tell these people that they have to learn, whether they like it or not. It'll improve everyone's life. And waving your gun around is no good.

Referencias

  • Bryan Greene "The Elegant Universe:Superstrings, Hidden Dimensions, and the Quest for the Ultimate Theory. ISBN 978-0375708114
  • Andréi Karpov. Feelings confirmed by numbers. http://www.viva64.comhttps://pvs-studio.com/en/blog/posts/0158/