Wrong (strange outputs) of real numbers

I am new in fortran and I am really curios about these strange outputs:
real*8 z1=0.7 but it shows 0.69999998807907104
Meanwhile y1 and y2 should be equal, but they have very small diference in numbers on the end.
Can someone explain this? Something wrong with my pc or with compiler? I am using tdm gcc compiler and Modern fortran

Your compiler uses 32 bits real by default.

Try z1=0.7d0 to use double precision (64 bits). The last digit may still be false, but it is normal, real numbers are stored with a limited number of bits in your CPU and in binary. And for example 0.7 has an infinite number of digits in base 2:

0.7 = 0b0.10110011001100110011001100110011001100110011001101...

Note also there are better ways to declare real numbers in modern Fortran. See that discussion:

And see also that tool:

2 Likes

Thank you for reply!
It’s really strange because I downloaded x64 version and compile uses 32 bit by default. Maybe you know the way how to change it?

Note also there are better ways to declare real numbers in modern Fortran. See that discussion:

Thanks for this too!

Some compilers have options to promote single to double precision, but it is not recommended to use such options for new code. It is better to follow the advice given.

The results for y1 and y2 agree to 15 significant digits. If anything is wrong, it is that you expect more precision than is reasonable for 64-bit real arithmetic.

Similarly, the single precision values 0.7 and 0.699999988 agree to better than 8 decimal digits, which means that they are the same as far as the computer is concerned.

So better leave it work on 32bit?
And what does mean “new code”? When change to x64 is ok?

If anything is wrong, it is that you expect more precision than is reasonable for 64-bit real arithmetic

Oh, sorry for stupid questions, but didn’t my compiler uses 32-bit (as it mentioned above)?
Or you are talking about another thing?

Similarly, the single precision values 0.7 and 0.699999988 agree to better than 8 decimal digits, which means that they are the same as far as the computer is concerned.

Can you explain more detail, please? I just can’t understand how 0.7 cann’t be equal to 0.7

Hello,

in floating point numbers, most values can not be described with perfect precision, but instead the closest number is chosen.

Let me give an example in the decimal system assuming 3 dogits after the comma.

The next value of 10.000 Ă· 3.000 = 3.333. Obviously this value is not an exact answer.

The same is true for the binary format used by actual compilers, where most values can not be expressed with perfect accuracy, even if they can in the decimal system.

Furthermore the term on the right-hand side of the “=” operator is evaluated before it is copied. So if there are ony 7 valid digits on tve right-hand side, the accuravy of the left-hand side will not be better even if the data tpe on the left-hand side woulf in principle allow for more precision.

I also think that you are currently confusing the address space of the compiled programs (e.g., 386 => 32 bit and x86-64 => 64 bit) and the space used for storing a floating point variable. Basically all current Fortran compilers (no matter whether 32 bit or 64 bit) support both single precision ( usually 32 bit) and double precision floating point variables (usually 64 bit).

You should make sure that either the precision of your variables and constants is consistent, or know the reason why you deliberately deviate from that principle.

Cheers,
Johann

1 Like

No, use “double precision” as follows. Define a module where you define dp, the double precision kind (it could be named something else).

module kind_mod
implicit none
private
public :: dp
integer, parameter :: dp = selected_real_kind(15, 307)
! integer, parameter :: dp = selected_real_kind(32) ! to get quadruple precision
end module kind_mod

See here for an explanation. Then use that definition in other program units, for both variable declarations and in floating point literals, for example

program main
use kind_mod, only: dp
implicit none
real(kind=dp), parameter :: pi = 3.141592653589793238462643_dp
end program

New code means code that you write. It is possible that some old code declared variables as plain real but relied on a compiler option promoting real to double precision.

2 Likes

Go to a page that provides a utility to convert between decimal and binary real numbers. Enter “0.7” and see what that gets converted to.

If you want a good, solid background in binary floating point arithmetic, set aside some time and read Goldberg’s article. Also worth reading: a related article on the issues that arise when converting a floating point number from/to base 2 and 10, by Steel and White.

3 Likes

Another page about using floating points in Fortran:
https://fortran-lang.org/en/learn/best_practices/floating_point/

Don’t confuse the hardware architecture and memory addressing, which is always 64 bits on usual machines nowadays, and the length of the numeric variables (either integer or floating point).

Your compiler generates binary for the x64 architecture, but most of time Fortran compilers define the default real and integer type as 32 bits. You can get 64 bits reals by declaring them as “double precision” instead of “real”, or with the kind mechanism described by @Beliavsky

Besides: What Every Programmer Should Know About Floating-Point Arithmetic

1 Like

Hello Johann!
Big thanks for your explanation.
To be honest, the main reason I decided to create this thred is because I asked my classmate, who uses compiler given by teacher, to check real*8 0.7 number. And he had output like 0.700000000000001
I has got really confused because it looks more accurate than outputs I have. Moreover that compiler 1995’s and it is really strange that it gives more accuracy. I don’t know, maybe it’s him has something wrong/special?

And one more question. I have tried double 0.7 in c#, and got equal output in console. Am I understand correctly, that c# doesn’t imply work with such numbers and he is just round numbers like this by default?

Indeed.

Even Microsoft’s Fortran compiler for CP/M, which ran on 8-bit computers, supported 16-bit and 32-bit integers, 32-bit and 64-bit reals. The original IBM-PC used an 8088 CPU, which had 16-bit integers and addresses, and 8-bit bus, supported the 8087 coprocessor with its 32-, 64- or 80-bit reals.

No single number can represent all the aspects of a modern CPU. Some current processors can work with 256 or 512 bit real vectors. Commonly, when we say “64-bit”, we mean “64-bit” address and “64-bit” integer capabilities.

Well, as I said I’m kinda beginner in Fortran, and for now I have very small imagination about what modules are, but anyway thank you in advance! :slight_smile:

No, use “double precision” as follows.
New code means code that you write. It is possible that some old code declared variables as plain real but relied on a compiler option promoting real to double precision.

Got it, thanks

Fortran, C, and C++ support separate compilation of program units. You can define procedures (functions and subroutines) in one file, compile that file to an object file, and compile the main program that calls those procedures separately. Then the object files are linked to create an executable. A common bug in Fortran before modules existed was that a procedure would be called with the wrong type of argument and give wrong results. By putting all procedures in modules, as is now recommended, you enable the compiler to check that the right types of arguments are being passed.

So even beginners should learn about and use modules.

@seme4ka008 ,

Many have upthread provided with a lot of good explanations with good theory as well.

Practically considering your computations, using current standard Fortran, one possible way to approach them is as follows where you, as the programmer upfront sets up a working precision (WP) for the floating-point arithmetic and uses it consistently in all the instructions. Please take a look - note the _WP suffix with all literal numerical values such as 0.57:

    integer, parameter :: WP = selected_real_kind( p=12 ) ! select precision of at least 12 digits 

    ! Variables
    real(kind=WP) :: x, y, y1, y2, z1, z2

    ! Constants
    real(kind=WP), parameter :: ONE = 1.0_wp
    real(kind=WP), parameter :: TWO = 2.0_wp
    real(kind=WP), parameter :: a = 0.57_wp
    real(kind=WP), parameter :: sqrt_a = sqrt(a)

    ! Calculations
    y1 = ONE/(TWO*(ONE + sqrt_a)) + ONE/(TWO*(ONE - sqrt_a)) -  &
         (a**2 + TWO)/(ONE - a**3)
    y2 = -ONE/(a**2 + a + ONE)

    z1 = 0.7_wp

    print *, "y1 = ", y1
    print *, "y2 = ", y2
    print *, "z1 = ", z1

end
C:\temp>ifort /free /standard-semantics p.f
Intel(R) Fortran Intel(R) 64 Compiler Classic for applications running on Intel(R) 64, Version 2021.8.0 Build 20221119_000000
Copyright (C) 1985-2022 Intel Corporation.  All rights reserved.

Microsoft (R) Incremental Linker Version 14.31.31105.0
Copyright (C) Microsoft Corporation.  All rights reserved.

-out:p.exe
-subsystem:console
p.obj

C:\temp>p.exe
 y1 =  -0.527732334160114
 y2 =  -0.527732334160114
 z1 =  0.700000000000000

Note in Fortran parlance, a “double precision” is considered to have a precision of at least 12 decimal digits in floating-point instructions.

No, just that when printing the number, it probably truncates the least significant digits and rounds accordingly.

No, just that when printing the number, it probably truncates the least significant digits and rounds accordingly.

Yep, as I thought

Thank you for detailed reply. I think I have understood these things, but I have tried your code and it gives me a little bit different from your result. I can see that you are using ifort. Is it the reason for another output?