9

Так у меня есть функция, которая выглядит примерно так:Может ли каким-то образом изменить переменные?

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

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

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

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

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

ответ

18

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

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

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

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

+0

Существует также примечание GCC для этого на http://gcc.gnu.org/wiki/x87note Из-за этого замечательного поведения сравнение вычислений с плавающей запятой также по своей сути нарушается, за исключением использования предварительно вычисленных значений. – hazzen 2008-09-08 19:57:12

3

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

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

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

0

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

2

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

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

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

1

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

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

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