Действительное значение после присваивания отличается от реального выражения

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

Строки следующие (qk был предопределен, а qk1 не был):

   print*,"qk",qk     
   print*,"qk1",qk1
   QK1=QK
   print*,"qk",qk
   print*,"qk1",qk1

и я печатаю эти значения:

    qk   21909779.000000000     
    qk1   6.44842193E+32
    qk   21909779.000000000     
    qk1   21909780.0 

Дело в том, что я ожидал получить qk1 равным qk... почему они разные? Когда я пытаюсь воспроизвести его, очевидно, я получаю те же самые значения.

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


person scana    schedule 19.01.2020    source источник
comment
определены ли qk и qk1 как точно такие же тип и точность? Количество десятичных нулей может указывать на разницу в точности   -  person rvbarreto    schedule 19.01.2020
comment
Вероятно, это не имеет значения, но первая показанная строка вызывает вывод значения переменной с именем qt. Я всегда подозреваю, что то, что может быть небрежной опечаткой при публикации кода в SO, является результатом небрежности при кодировании...   -  person High Performance Mark    schedule 19.01.2020
comment
@rvbarreto qt имеет двойную точность, а qk1 не определен   -  person scana    schedule 19.01.2020
comment
@HighPerformanceMark да, вы правы, код правильный, но я сделал ошибку при копировании. сейчас поправлю.   -  person scana    schedule 19.01.2020
comment
@rvbarreto вот оно что! Как только я определил qk1 как двойную точность, я получаю те же значения. При переводе я считал само собой разумеющимся, что qk1 определен, но это не так. Хорошо, я решил проблему, большое спасибо вам обоим. Тем не менее, мне все еще любопытно... почему фортран добавил его?   -  person scana    schedule 19.01.2020
comment
Таким образом, вероятно, существует разница между типом данных qk и qk1 (реальная и двойная точность), но чтобы это можно было сказать, добавьте минимальный рабочий пример (MWE), иллюстрирующий вашу проблему.   -  person albert    schedule 19.01.2020
comment
Нет времени проверять в данный момент, но вы близки к максимальным цифрам, которые может обеспечить одиночная точность, я подозреваю, что из-за этого вы не можете точно представить число, и поэтому вы получаете ближайшее приближение, равное 1 прочь. см. stackoverflow. com/questions/56968179/ и stackoverflow.com/questions/59581069/ для чего-то подобного   -  person Ian Bush    schedule 19.01.2020
comment
Ваш компилятор пытался исправить ошибку за вас и использовал другую точность числа. Взгляните на статью в Википедии «Формат с плавающей запятой одинарной точности», а также «Формат с плавающей запятой двойной точности» и обратите внимание на то, как IEEE 754 определяет представление чисел. Это может помочь понять ограничения, связанные с выбранной вами точностью, а также с конверсиями.   -  person rvbarreto    schedule 19.01.2020
comment
Кроме того, есть флаги, которые вы можете использовать в своем компиляторе, чтобы предотвратить это. Если вы используете gfortran, взгляните на staff.washington. edu/rjl/uwamath583s11/sphinx/notes/html/ в Wconversion и другие флаги   -  person rvbarreto    schedule 19.01.2020
comment
Если вы проверите значение spacing(qk1), вы обнаружите, что оно равно 2. Другими словами, значения с плавающей запятой одинарной точности в этой области либо все нечетные, либо все четные. В этом случае кажется, что они все даже.   -  person RussF    schedule 20.01.2020
comment
Число с плавающей запятой IEEE-binary32 может точно представлять все целые числа между ± 2 ^ 24 (± 16777216), число, которое вы пытаетесь представить, больше, и, следовательно, произойдет округление. qk, скорее всего, является числом двойной точности, которое может содержать это значение, но qk1 имеет более низкую точность. Отсюда и округление.   -  person kvantour    schedule 20.01.2020


Ответы (1)


Причина в том, что, как указано в комментариях, qk — одинарная точность, qk1 — двойная, а при требуемых значениях интервал между вещественными числами одинарной точности равен 2:

Program one_out

  Use, Intrinsic :: iso_fortran_env, Only : real32, real64

  Implicit None

  Real( real64 ) :: qk64
  Real( real32 ) :: qk32

  qk64 = 21909779.0_real64
  qk32 = qk64

  Write( *, * ) 'qk64 = ', qk64
  Write( *, * ) 'qk32 = ', qk32

  Write( *, * ) '64 spacing ', Spacing( qk64 )
  Write( *, * ) '32 spacing ', Spacing( qk32 )

End Program one_out

ian@eris:~/work/stack$ gfortran -Wall -Wextra -fcheck=all -std=f2008 one_out.f90 
one_out.f90:11:9:

   qk32 = qk64
         1
Warning: Possible change of value in conversion from REAL(8) to REAL(4) at (1) [-Wconversion]
ian@eris:~/work/stack$ ./a.out
 qk64 =    21909779.000000000     
 qk32 =    21909780.0    
 64 spacing    3.7252902984619141E-009
 32 spacing    2.00000000    

Самый важный урок здесь всегда, всегда используйте Implicit None, а второй заключается в том, что предупреждения компилятора полезны, включите их и разберитесь, что они означают!

person Ian Bush    schedule 20.01.2020
comment
Интересно, поможет ли объяснение/ссылка на уровне спрашивающего, что такое интервал (или почему это важно в данном случае)? - person francescalus; 20.01.2020
comment
ОК - встречаемся большую часть остального дня, но сойдет, или не стесняйтесь редактировать ответ самостоятельно - person Ian Bush; 20.01.2020
comment
Я бы, вероятно, написал что-то вроде «Лёгко, значение 21909779 попадает в промежуток между соседними представимыми числами». Этот пробел является упомянутым интервалом: с интервалом 2 между 21909778 и 21909780 невозможно точно присвоить значение 21909779. Но это немного чепуха без приличной ссылки. - person francescalus; 20.01.2020