Скачай PVS-Studio и найди ошибки в С, С++,C# коде
516,57
рейтинг
31 мая 2014 в 19:56

Разработка → Эффект последней строки

Copy-Paste
Я изучил множество ошибок, возникающих в результате копирования кода. И утверждаю, что чаще всего ошибки допускают в последнем фрагменте однотипного кода. Ранее я не встречал в книгах описания этого феномена, поэтому и решил о нём написать. Я назвал его «эффектом последней строки».

Введение


Меня зовут Андрей Карпов. Я занимаюсь необычным занятием. Я исследую программный код приложений с помощью статического анализатора и описываю найденные ошибки и недочёты. Занимаюсь я этим из-за прагматичных и корыстных побуждений. Так наша компания рекламирует инструмент PVS-Studio. Нашли ошибки. Описали в статье. Привлекли внимание. Profit. Но сегодня статья не про анализатор.

В процессе анализа проектов я сохраняю замеченные ошибки и соответствующие фрагменты кода в специальной базе. Кстати, с содержимым этой базы может познакомиться любой желающий. Мы превращаем её в набор html-страниц и выкладываем их на сайте в разделе "Выявленные ошибки".

База уникальна! Сейчас она содержит около 1500 фрагментов кода с ошибками. Она ждёт людей, которые смогут выявить в этих ошибках закономерности. Это может послужить основой многих исследований и материалов для книг и статей.

Специально я не делал никаких исследований накопленного материала. Тем не менее одна закономерность так явно проявляет себя, что я решил изучить её подробнее. В статьях мне часто приходится писать «обратите внимание на последнюю строку». Я решил, что это не спроста.

Эффект последней строки


В исходном коде программы часто нужно написать несколько однотипных конструкций, идущих подряд. Набирать несколько раз однотипный код скучно и не эффективно. Поэтому используется метод Copy-Paste. Фрагмент кода копируется несколько раз, после чего делаются правки. Все знают, что этот метод плох. Легко забыть что-то поменять и в результате код будет содержать ошибку. К сожалению, часто хорошей альтернативы нет.

Теперь о закономерности. Я выяснил, что чаще всего ошибка допускается в самом последнем скопированном блоке кода.

Простой короткий пример:
inline Vector3int32& operator+=(const Vector3int32& other) {
  x += other.x;
  y += other.y;
  z += other.y;
  return *this;
}

Обратите внимание на строчку «z += other.y;». В ней забыли поменять 'y' на 'z'.

Пример кажется искусственным. Но этот код взят из реального приложения. Далее я убедительно покажу, что это очень частая и распространенная ситуация. Именно так и выглядит «эффект последней строки». Человек чаще всего допускает ошибку в самом конце внесения однотипных исправлений.

Я где-то слышал, что часто альпинисты срываются на последних десятках метрах подъема. Не потому, что они устали. Просто они радуются, что осталось совсем немного. Они предвкушают сладкий вкус победы над вершиной. В результате они ослабляют внимание и допускают роковую ошибку. Видимо, что-то такое случается и с программистами.

Теперь немного цифр.

Изучив базу ошибок, я выявил 84 фрагмента кода, которые, как мне кажется, были написаны с использованием Copy-Paste. Из них 41 фрагмент содержит ошибку где-то в середине скопированных блоков. Вот пример:
strncmp(argv[argidx], "CAT=", 4) &&
strncmp(argv[argidx], "DECOY=", 6) &&
strncmp(argv[argidx], "THREADS=", 6) &&
strncmp(argv[argidx], "MINPROB=", 8)) {

Длина строки «THREADS=» не 6, а 8 символов.

В остальных 43 случаях ошибка была найдена в самом последнем скопированном блоке.

На первый взгляд, число 43 совсем немного больше 41. Но учтите, что однотипных блоков бывает достаточно много. И ошибка может быть в первом, втором, пятом или даже в десятом блоке. Получаем относительно ровное распределение ошибок в блоках и резкий скачок в конце.

В среднем я взял, что количество однотипных блоков равно 5.

Получается, что на первые 4 блока приходится 41 ошибка. Или около 10 ошибок на один блок.

На последний пятый блок приходится 43 ошибки!

Для наглядности можно построить вот такой приблизительный график:

Рисунок 1. Приблизительный график количества ошибок в пяти блоках похожего кода.

Рисунок 1. Приблизительный график количества ошибок в пяти блоках похожего кода.

Получается:

Вероятность допустить ошибку в последнем скопированном блоке в 4 раза больше, чем в любом другом.

Каких-то грандиозных выводов я из этого не делаю. Просто интересное наблюдение. С практической точки зрения полезно знать о нём. Тогда можно заставить себя не расслабляться в самом конце.

Примеры


Осталось убедить читателей, что это не мои фантазии, а реалии жизни. Для этого я продемонстрирую примеры, которые подтвердят мои слова.

Все примеры я, конечно, приводить не буду. Ограничусь наиболее простыми или показательными.

Source Engine SDK


inline void Init( float ix=0, float iy=0,
                  float iz=0, float iw = 0 ) 
{
  SetX( ix );
  SetY( iy );
  SetZ( iz );
  SetZ( iw );
}

В конце нужно было вызвать функцию SetW().

Chromium


if (access & FILE_WRITE_ATTRIBUTES)
  output.append(ASCIIToUTF16("\tFILE_WRITE_ATTRIBUTES\n"));
if (access & FILE_WRITE_DATA)
  output.append(ASCIIToUTF16("\tFILE_WRITE_DATA\n"));
if (access & FILE_WRITE_EA)
  output.append(ASCIIToUTF16("\tFILE_WRITE_EA\n"));
if (access & FILE_WRITE_EA)
  output.append(ASCIIToUTF16("\tFILE_WRITE_EA\n"));
break;

Совпадает последний и предпоследний блок.

ReactOS


if (*ScanString == L'\"' ||
    *ScanString == L'^' ||
    *ScanString == L'\"')

Multi Theft Auto


class CWaterPolySAInterface
{
public:
    WORD m_wVertexIDs[3];
};
CWaterPoly* CWaterManagerSA::CreateQuad (....)
{
  ....
  pInterface->m_wVertexIDs [ 0 ] = pV1->GetID ();
  pInterface->m_wVertexIDs [ 1 ] = pV2->GetID ();
  pInterface->m_wVertexIDs [ 2 ] = pV3->GetID ();
  pInterface->m_wVertexIDs [ 3 ] = pV4->GetID ();
  ....
}

Последняя строчка написана по инерции и является лишней. В массиве всего 3 элемента.

Source Engine SDK


intens.x=OrSIMD(AndSIMD(BackgroundColor.x,no_hit_mask),
                AndNotSIMD(no_hit_mask,intens.x));
intens.y=OrSIMD(AndSIMD(BackgroundColor.y,no_hit_mask),
                AndNotSIMD(no_hit_mask,intens.y));
intens.z=OrSIMD(AndSIMD(BackgroundColor.y,no_hit_mask),
                AndNotSIMD(no_hit_mask,intens.z));

В последней строке забыли заменить «BackgroundColor.y» на «BackgroundColor.z».

Trans-Proteomic Pipeline


void setPepMaxProb(....)
{  
  ....
  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 ) ... }
  ....
}

В последнем условии забыли заменить «prob > max6» на «prob > max7».

SeqAn


inline typename Value<Pipe>::Type const & operator*() {
  tmp.i1 = *in.in1;
  tmp.i2 = *in.in2;
  tmp.i3 = *in.in2;
  return tmp;
}

SlimDX


for( int i = 0; i < 2; i++ )
{
  sliders[i] = joystate.rglSlider[i];
  asliders[i] = joystate.rglASlider[i];
  vsliders[i] = joystate.rglVSlider[i];
  fsliders[i] = joystate.rglVSlider[i];
}

В последней строке надо было использовать массив rglFSlider.

Qt


if (repetition == QStringLiteral("repeat") ||
    repetition.isEmpty()) {
  pattern->patternRepeatX = true;
  pattern->patternRepeatY = true;
} else if (repetition == QStringLiteral("repeat-x")) {
  pattern->patternRepeatX = true;
} else if (repetition == QStringLiteral("repeat-y")) {
  pattern->patternRepeatY = true;
} else if (repetition == QStringLiteral("no-repeat")) {
  pattern->patternRepeatY = false;
  pattern->patternRepeatY = false;
} else {
  //TODO: exception: SYNTAX_ERR
}

В самом последнем блоке забыли про 'patternRepeatX'. Должно быть:
pattern->patternRepeatX = false;
pattern->patternRepeatY = false;

ReactOS


const int istride = sizeof(tmp[0]) / sizeof(tmp[0][0][0]);
const int jstride = sizeof(tmp[0][0]) / sizeof(tmp[0][0][0]);
const int mistride = sizeof(mag[0]) / sizeof(mag[0][0]);
const int mjstride = sizeof(mag[0][0]) / sizeof(mag[0][0]);

Переменная 'mjstride' будет всегда равна единицы. Последняя строчка должна быть такой:
const int mjstride = sizeof(mag[0][0]) / sizeof(mag[0][0][0]);

Mozilla Firefox


if (protocol.EqualsIgnoreCase("http") ||
    protocol.EqualsIgnoreCase("https") ||
    protocol.EqualsIgnoreCase("news") ||
    protocol.EqualsIgnoreCase("ftp") ||          <<<---
    protocol.EqualsIgnoreCase("file") ||
    protocol.EqualsIgnoreCase("javascript") ||
    protocol.EqualsIgnoreCase("ftp")) {          <<<---

Подозрительная строка «ftp» в конце. С этой строкой уже сравнивали.

Quake-III-Arena


if (fabs(dir[0]) > test->radius ||
    fabs(dir[1]) > test->radius ||
    fabs(dir[1]) > test->radius)

Не проверили значение из ячейки dir[2].

Clang


return (ContainerBegLine <= ContaineeBegLine &&
        ContainerEndLine >= ContaineeEndLine &&
        (ContainerBegLine != ContaineeBegLine ||
         SM.getExpansionColumnNumber(ContainerRBeg) <=
         SM.getExpansionColumnNumber(ContaineeRBeg)) &&
        (ContainerEndLine != ContaineeEndLine ||
         SM.getExpansionColumnNumber(ContainerREnd) >=
         SM.getExpansionColumnNumber(ContainerREnd)));

В самом конце выражение «SM.getExpansionColumnNumber(ContainerREnd)» сравнивается само с собой.

MongoDB


bool operator==(const MemberCfg& r) const {
  ....
  return _id==r._id && votes == r.votes &&
         h == r.h && priority == r.priority &&
         arbiterOnly == r.arbiterOnly &&
         slaveDelay == r.slaveDelay &&
         hidden == r.hidden &&
         buildIndexes == buildIndexes;
}

Забыли в самом конце про «r.».

Unreal Engine 4


static bool PositionIsInside(....)
{
  return
    Position.X >= Control.Center.X - BoxSize.X * 0.5f &&
    Position.X <= Control.Center.X + BoxSize.X * 0.5f &&
    Position.Y >= Control.Center.Y - BoxSize.Y * 0.5f &&
    Position.Y >= Control.Center.Y - BoxSize.Y * 0.5f;
}

В последней строке забыли сделать 2 правки. Во-первых, надо заменить ">=" на "<=. Во-вторых, заменить минус на плюс.

Qt


qreal x = ctx->callData->args[0].toNumber();
qreal y = ctx->callData->args[1].toNumber();
qreal w = ctx->callData->args[2].toNumber();
qreal h = ctx->callData->args[3].toNumber();
if (!qIsFinite(x) || !qIsFinite(y) ||
    !qIsFinite(w) || !qIsFinite(w))

В самом последнем вызове функции qIsFinite, нужно использовать в качестве аргумента переменную 'h'.

OpenSSL


if (!strncmp(vstart, "ASCII", 5))
  arg->format = ASN1_GEN_FORMAT_ASCII;
else if (!strncmp(vstart, "UTF8", 4))
  arg->format = ASN1_GEN_FORMAT_UTF8;
else if (!strncmp(vstart, "HEX", 3))
  arg->format = ASN1_GEN_FORMAT_HEX;
else if (!strncmp(vstart, "BITLIST", 3))
  arg->format = ASN1_GEN_FORMAT_BITLIST;

Длина строки «BITLIST» не 3, а 7 символов.

На этом остановимся. Думаю, приведённых примеров более чем достаточно.

Заключение


В этой статье вы узнали, что при использовании Copy-Paste вероятность допустить ошибку в последнем скопированном блоке в 4 раза выше, чем в любом другом.

Это особенность психологии человека, а не его профессиональных навыков. В статье мы увидели, что к тяге к ошибкам в конце склонны даже высококвалифицированные разработчики таких проектов, как Clang или Qt.

Надеюсь моё наблюдение окажется полезным. И, возможно, подтолкнет людей к исследованию накопленной базы ошибок. Думаю, она позволит найти много интересных закономерностей и сформулировать новые рекомендации для программистов.

Эта статья на английском


Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Andrey Karpov. The Last Line Effect.

Прочитали статью и есть вопрос?
Часто к нашим статьям задают одни и те же вопросы. Ответы на них мы собрали здесь: Ответы на вопросы читателей статей про PVS-Studio и CppCat, версия 2014. Пожалуйста, ознакомьтесь со списком.
Автор: @Andrey2008
PVS-Studio
рейтинг 516,57
Скачай PVS-Studio и найди ошибки в С, С++,C# коде

Комментарии (50)

  • +21
    «В редакторе настоящего программиста должна отсутствовать функция копирования блоков [текста]» © Чарльз Мур (?) :)
    • +11
      "… настоящего программиста на лиспе..."
      Fix
      • +4
        Чарльз Мур — фортер. Первый фортер. В какой-то степени это «антилиспер» :) И описанное правило было одним из главных идеологических камней, на которых строился Форт :)
  • +3
    Да, как люди только ни страдают лишь бы не использовать лисп^Wмакросы и ФП.
    • НЛО прилетело и опубликовало эту надпись здесь
    • +1
      Посмотрел бы я на вас в попытке найти ошибку в порождающей функции хотя бы 3-го уровня :-)
      P.S. Как ни странно, часто наблюдается в JavaScript'ах.
  • +13
    Не, серьезно, пора делать постинг ачивок в соцсетях: «30 дней без ошибок в последней строке», «Проект похвалили ребята из PVS-Studio».

    Главное, что это не обидно будет, ачивка-то будет об отсутствии косяков, а не об их наличии!

    А если серьезно, то крупным проектам прямо хорошим тоном было бы заказывать у вас проверку их трудов — может, вы и не 100% косяков выявите, но сторонним взглядом посмотрите, а то и других статьей научите, все же профит!
    • +6
      Лучше бы, конечно, «ХХ дней без копипасты» :)
      • +4
        А что, кстати, прикольная идея. Отслеживать Ctrl-C/V, смотреть по заголовку текущего окна что это редактор кода, и проигрывать mp3-шку с фразой «Ахтунг, копипаст кода!». :) Фрилансерам это будет так, напоминалка для контроля за собой, а вот если послушать в офисах…
  • +2
    Риск получить нештатную ситуацию всегда выше в крайних точках, когда начинаешь и когда заканчиваешь. Я стараюсь про это не забывать в любом деле.
  • +12
    OpenSSL… Длина строки «BITLIST» не 3, а 7 символов...

    Меня это настораживает — там уже где то был баг где длина буфера неправильная была :)
    • +1
      Меньше — не больше… Да и операция сравнения несколько безопаснее операции чтения блока памяти.
      • 0
        Эээ… А чем сравнение не чтение?
        • 0
          Злоумышленник получает меньше данных.
      • 0
        Ага, особенно прикольно сравнить меньше символов где-нибудь в checksum :-)
        И понизить криптостойкость на несколько порядков.
        • 0
          Криптостойкость не понизится, вместо этого просто все перестанет работать. Чем хороши алгоритмы подсчета контрольной суммы — они проверяют сами себя (при условии наличия независимой реализации).
  • +9
    Глупый вопрос: как вообще это всё работает? Вы уже немало статей написали с явными ошибками. Не синтаксическими, а логическими. Если их ещё не исправили, значит они не заметны и программа вроде как работает. Почему? Ведь надёжность сложной системы, составленной из блоков, меньше, чем сумма надёжностей этих блоков?
    • +56
      Едет ли машина у которой не горят фары?
      • +12
        Весьма краткий и точный ответ. :)
      • +34
        Самое отвратительное даже не то, что она едет, а что если ездить только днём то даже не узнаешь, что с ней что-то не так.
        • –3
          В нормальных машинах есть индикаторы, которые оповещают водителя о проблемах, в том числе о перегоревших (не горящих) фарах. Такой же эффект принесут юнит-тесты в разрабатываемом приложении.
          • +4
            Наличие тестов тоже не гарантия: Как статический анализ дополняет TDD :).
            А некоторые вещи вообще юнит-тестами поймать практически невозможно: V596, V597.
            • 0
              Вот 596й не совсем правильный пример — хорошие тесты должны покрывать и «плохие» случаи тоже. Отсутствие искдючения -> тест провален.
              А вот 597й да, плохо. При сборке тестов если проверять на чистоту на выходе — оптимизатор не выкинет memset; а в релизе может и выкинуть.
              • 0
                На практике 596 покрыть крайне сложно и никто это не делает. Что-бы в нужный момент new кинул исключение, нужна целая система.
                • 0
                  А как же «ни коммита без 100% покрытия»?
                  • +2
                    Это в параллельной вселенной. На практике, даже в те, кто стараются, обычно покрывают только 80%.
                    • 0
                      Глянул сейчас единственный проект со спеками, «1682 / 2300 LOC (73.13%) covered.». Грустно, товарищи :)
                  • +3
                    Кроме того, при желании не сложно обеспечить формально 100% (или близко к этому) покрытия тестами, которые по сути не особо полезны — то, что строчка кода выполнилась в процессе работы теста не даёт гарантии, что вся функциональность и все возможные случаи действительно были протестированы. Так что «покрытие» полезно только тем, что показывает какой код точно не тестируется, но ничего полезного не говорит про качество тестирования покрытого кода.
          • +2
            Что значит «в нормальных»? Где эта норма? Я повидал с десяток машин разной стоимости — от 300 тысяч до более миллиона. И только в одной есть датчик неработающих фар.
      • –1
        Веет дао :-)
    • 0
      По себе могу сказать — иногда просто везет. Например, по милости компилятора (или согласно своей-же крюкописи где-то в другом месте в коде) переменная, которой сам забыл присвоить значение (как, например, здесь: pattern->patternRepeatY = false;), уже содержит нужное значение. Ошибку-то ты потом сам находишь, но только по коду, а в работе программы она не проявляется. Вот так и живем. ))
  • +1
    А есть статистика, сколько ошибок происходит из-за копипасты по сравнению с прочими, в процентах от общего кол-ва? Хотя бы примерно.
    • 0
      Не считал. Но думаю прилично. В статье упоминается 84 случая (из 1500), где явно заметен множественный копипаст. А ведь есть ещё однократные копипасты функций или, скажем, веток.
  • +23
    Хорошо, что анализатора во времена Quake не было, иначе бы ни распрыжки, ни овербаунсов…
    • 0
      Это баги, а не заложенная особенность физики? Про это где-то можно почитать?
      • +2
        Вроде бы сначала это были особенности реализации (непреднамеренные), а в последующих версиях такие вещи стали закладывать как фичу, потому что киберспортсмены и любители триксов (распрыжки и тд) привыкли.
      • +2
        Впервые распрыжка появилась в quake 2 как баг, но из-за того, что он сильно вошел в моду, в 3й части его сделали фичей. Другой баг — двойной прыжок (double jump) был «исправлен», однако появился новый — overbounce, ситуация, когда при столкновении с поверхностью вектор скорости отражался либо в противоположную сторону, либо в другую плоскость. Распрыжка была переполнением, в 2000х был блог у разработчиков промода, они там это описывали. Сейчас только гуглить остается.
        • 0
          Ну здрасте! Распрыжка появилась ещё в Quake I, а с физикой QuakeWorld получила всеобщее распространение. Самая круть была в Quake TF ворваться на базу врага разогнанным heavy guy с 300 красной броней на скорости в 1000 единиц и пролететь сквозь взрывающиеся поля и гранаты, забрать флаг и унестись к себе на базу =)
  • +3
    Я где-то слышал, что часто альпинисты срываются на последних десятках метрах подъема. Не потому, что они устали. Просто они радуются, что осталось совсем немного. Они предвкушают сладкий вкус победы над вершиной. В результате они ослабляют внимание и допускают роковую ошибку.

    Я в Super Hexagon так играю. Проигрываю часто на плюс-минус 00:59.50 (там одна минута — «победа»).
  • +4
    ContainerEndLine != ContaineeEndLine
    За такие названия переменных нужно бить по рукам. Различие в одну букву в середине слова совершенно не видно при беглом чтении кода. Да что там, чтобы это заметить даже при внимательном чтении, мне пришлось сравнивать названия переменных побуквенно!
    • +2
      BTW, вполне тянет на ещё один критерий проверки — похожесть имён переменных в одной области видимости. А если они ещё и одного типа — совсем аларм.
      • 0
        Даже не одного типа, а разных типов с возможностью неявной конвертации.
    • 0
      Я сразу заметил разницу. Мне они даже не показались похожими.
      P.S. Я много читаю и привык глотать слова целиком, всегда замечаю отличие даже в одну букву
      • +4
        Видимо, это код писал человек, подобный вам )
        P.S. Я много читаю и привык глотать слова целиком, не замечая ошибок в одной букве…
        • 0
          Ну значит нужно ещё и словарный запас подтянуть. *er vs *ee — это довольно частая фишка в английском. Contrainer vs Containee, Employer vs Employee и т.д. и т.п. Путать их ни в коем случае нельзя. Да и какая тут «середина слова» к бесу? Это ж последняя буква! Тут скорее камешек в огород CamelCaseIdentifiers…
          • +1
            Словарный запас тут ни при чём, я знаю об *er и *ee. Мы сейчас работаем над поддержкой WebRTC в нашем приложении, и было бы логично назвать звонящего Caller, а отвечающего — Callee. Но не всё то, что логично, ещё и удобно. На слух слова указанными выше окончаниями чётко различимы, тогда как при чтении ошибиться очень легко, а при написании — и подавно: латинские буквы E и R расположены на клавиатуре рядом. В результате мы вообще не стали использовать слово Callee, и, т.к. в коде нужно только знать, кто инициировал звонок, назвали звонящего initiator. Всё-таки, мы заботимся, по возможности, о читающих наш код коллегах.

            Про середину слова вы заметили верно, но, по сути, просто придрались к словам (нечаянный каламбур), т.к. буквы различаются в середине идентификатора. Великая разница.

            И CamelCase тут не виноват. Вот это
            container_end_line != containee_end_line
            
            не очень сильно улучшает читабельность кода. Я искренне рад за вас, раз вы обладаете способностью мгновенно обнаруживать подобные отличия в одну букву, но увы, я и множество (не в математическом смысле) других людей обделены этим даром. Выбирать чётко различимые названия переменных — одно из правил хорошего тона в разработке ПО.
  • +3
    Будучи перфекционистом, давно, еще в ВУЗе заметил, что очень часто ошибаюсь при копи-пастинге.
    Очень скоро стало жалко тратить время на отладку и поиск «детских» ошибок и была выработана методика по «недопущению» таких ошибок.
    Поделюсь, может кому-нибудь сбережет немного времени и нервов.
    — Как только нажали Ctrl+C, выставляем (в уме или лучше в комментах) флаг «копи-паста»: //CP->
    — Пишем копи-пасту НАРУШАЯ ВСЕ правила форматирования кода — главное чтоб отличающиеся места были в одной колонке.
    — Закончив редактирование копи-пасты, составляем и сверяем акронимы отличающихся колонок слева и справа (если несколько, то все).
    — Только после того как убедились, что все верно, снимаем флаг копи-пасты. Но в коде, в комментах, оставляем метку копипасты: //CP

    Звучит сложно, на по факту, все делается быстро и почти на автомате.
    Для наглядности рассмотрим пример. Есть код (из примеров выше):

    intens.x=OrSIMD(AndSIMD(BackgroundColor.x,no_hit_mask),
                    AndNotSIMD(no_hit_mask,intens.x));
    intens.y=OrSIMD(AndSIMD(BackgroundColor.y,no_hit_mask),
                    AndNotSIMD(no_hit_mask,intens.y));
    intens.z=OrSIMD(AndSIMD(BackgroundColor.y,no_hit_mask),
                    AndNotSIMD(no_hit_mask,intens.z));
    

    Я бы писал так:
    intens.x = OrSIMD(AndSIMD(BackgroundColor.x, no_hit_mask), AndNotSIMD(no_hit_mask, intens.x));
    intens.y = OrSIMD(AndSIMD(BackgroundColor.y, no_hit_mask), AndNotSIMD(no_hit_mask, intens.y));
    intens.z = OrSIMD(AndSIMD(BackgroundColor.y, no_hit_mask), AndNotSIMD(no_hit_mask, intens.z));

    Получаем исходный акроним (восьмая колонка): xyz.
    Получаем остальные акронимы: xyy, xyz.
    Опечатка более-менее видна.

    В указанной методике важны все 4 пункта. Они дополняют друг-друга.
    Отсутствие любого из пунктов или сводит на нет эффективность, или приводит к неврозу «все ли копи-пасты проверены».
  • 0
    У меня какая то другая тенденция, при копипасте скорее во втором пункте ошибусь, чем в последнем)
  • 0
    Эклипс подсказывает, а не копипастная ли ошибка.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Самое читаемое Разработка