Начнем издалека… Есть в C++ встроенный тип size_t, который является “целым типом без знака, используемым реализацией для индексирования массивов” © Страуструп. То есть, если вы работаете с индексами в массивах и их длинами, то в общем-то “правильнее” использовать не int, что в большинстве случаев и делается, а именно size_t (для длин можно еще использовать ptrdiff_t - тип, который возвращает операция вычитания двух указателей). Вчера эта “правильность” для меня вышла боком…
size_t length;
...
for(size_t i = 0; i < length; ++i) {
double x = 2 * (i - length / 2) / length;
...
}
Вот такой был изначальный код. Через несколько минут после написания я вспомнил, что неплохо бы сделать так:
size_t length;
...
for(size_t i = 0; i < length; ++i) {
double x = 2 * static_cast<double>(i - length / 2) / length;
...
}
потому что при делении двух целых чисел у нас тоже целое будет, а мне нужно было как раз вещественное.
Но это еще ладно. После этого я отдал свой кусок лабораторной (мы ее пишем парами) своей девушке (а я как раз с ней пишу). Когда она добралась до этого кода и стала тестить его - у нее стали получаться какие-то странные, неправильные числа. Долгое время я копался в коде и не мог понять, в чем проблема, а потом до меня наконец-то дошло - разность двух беззнаковых чисел также будет беззнаковая (т.е. число -1 будет на самом деле 0xFFFFFFFF), после чего код превратился в такой:
size_t length;
...
for(size_t i = 0; i < length; ++i) {
double x = 2 * static_cast<double>(static_cast<int>(i) - static_cast<int>(length) / 2) / length;
...
}
После чего я на всякий случай еще полчаса проверял небольшую программу на вот такие ошибки - на всякий случай. К счастью, таких больше не нашлось, но неприятный осадок остался.
Выводы: или пишите не особо заморачиваясь на правильности употребления типов (везде используйте int и double, например), или пишите семантически правильно, но не делайте таких ошибок, как я… Может, конечно, это только я такой, но мне лично сложно было именно по коду определить, что результат будет получаться не такой как я хочу.
PS. Я теперь немного понимаю, почему в Java нет unsigned типов. :)






August 31st, 2008 at 12:11
Не хочу расстраивать, но вместо
2 * static_cast(i – length / 2) / length;
достаточно написать
2.0 * (i – length / 2) / length
А вообще static_cast используется для безопасного приведения классов. Для базовых типов смело можно писать (int)i-int(length/2). Уж тут-то компилятор справиться, можно не переживать :-)
September 4th, 2008 at 23:10
А индекс минус размер в итоге даёт индекс или всё-таки размер? Т.е. как бы нам от 3 помидоров отнять 2 огурца и получить 1 тыкву…. поэто без “кастов” не обошлось… :-) Проблема не в типах, а в некорректном их применении, что в данном случае и произошло. Пересмотрите выводы.