Можно ли как-то изменить переменные? - PullRequest
9 голосов
/ 08 сентября 2008

Итак, у меня есть функция, которая выглядит примерно так:

float function(){
    float x = SomeValue;
    return x / SomeOtherValue;
}

В какой-то момент эта функция переполняется и возвращает действительно большое отрицательное значение. Чтобы попытаться отследить, где именно это происходит, я добавил оператор cout, чтобы функция выглядела следующим образом:

float function(){
    float x = SomeValue;
    cout << x;
    return x / SomeOtherValue;
}

и это сработало! Конечно, я решил проблему с помощью двойного. Но мне любопытно, почему эта функция работала правильно, когда я следил за ней. Это типично, или может быть где-то еще ошибка, которую я пропускаю?

(Если это поможет, значение, хранящееся в float, является целочисленным, а не особо большим. Я просто помещаю его в float, чтобы избежать приведения.)

Ответы [ 5 ]

18 голосов
/ 08 сентября 2008

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

Это происходит из-за разницы между спецификацией IEEE и оборудованием, на котором работает код. Ваш процессор, вероятно, имеет 80-битные регистры с плавающей запятой, которые используются для хранения 32-битного значения с плавающей запятой. Это означает, что точность остается гораздо большей, пока значение остается в регистре, чем когда оно принудительно передается по адресу памяти (также известному как «возвращение в регистр»).

Когда вы передавали значение cout, компилятору приходилось записывать плавающую точку в память, что приводило к потере точности и интересному поведению в случаях переполнения WRT.

См. Документацию MSDN по VC ++ с плавающей запятой . Вы можете попробовать скомпилировать с / fp: strict и посмотреть, что произойдет.

3 голосов
/ 08 сентября 2008

Печать значения в cout никоим образом не должна изменять значение параметра.

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

Добавление оператора cout означает, что значение x используется напрямую. Без этого оптимизатор мог бы удалить переменную, поэтому изменил порядок вычисления и, следовательно, изменил ответ.

2 голосов
/ 08 сентября 2008

Кроме того, всегда полезно объявлять неизменяемые переменные, используя const:

float function(){
    const float x = SomeValue;
    cout << x;
    return x / SomeOtherValue;
}

Помимо прочего, это предотвратит непреднамеренную передачу переменных в функции, которые могут изменять их с помощью не const ссылок.

1 голос
/ 31 августа 2010

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

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

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

0 голосов
/ 08 сентября 2008

Я не думаю, что cout влияет на переменную, проблема должна быть в другом месте.

...