Question about double precision on ARM processors

Double precision variables in Fortran are said to be run as single precision real variables when run on ARM hardware . Fortran can currently be run offline on ubuntu and debian linux on the userland app on android 12 or 13.
Ian Martin Ajzenszmidt

1 Like

Floating Point Operations

ARM processors support both single precision and double precision floating point operations. However, the performance of double precision floating point operations can vary depending on the specific processor model.

Some ARM processors have dedicated hardware for double precision floating point operations, while others use software emulation. Processors with dedicated hardware for double precision floating point operations will generally have better performance.

It is important to check the documentation for your specific processor to see what level of support it provides for double precision floating point operations.

1 Like

userland@localhost:~ gfortran testarmdp.f userland@localhost:~ ./a.out
7.1100000494631581E+024
0.711000025
userland@localhost:~$ cat testarmdp.f
double precision dp /.711E25/

          real   sp  /.711/
          print *,dp
           print *,sp
           end program

userland@localhost:~ userland@localhost:~ lscpu
Architecture: aarch64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 4
On-line CPU(s) list: 0-3
Vendor ID: ARM
Model name: Cortex-A53
Model: 4
Thread(s) per core: 1
Core(s) per cluster: 4
Socket(s): -
Cluster(s): 1
Stepping: r0p4
CPU max MHz: 2001.0000
CPU min MHz: 850.0000
BogoMIPS: 26.00
Flags: fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid
Vulnerabilities:
Itlb multihit: Not affected
L1tf: Not affected
Mds: Not affected
Meltdown: Not affected
Spec store bypass: Not affected
Spectre v1: Mitigation; __user pointer sanitization
Spectre v2: Not affected
Srbds: Not affected
Tsx async abort: Not affected
userland@localhost:~$

These claims regarding double precision should be verified, else removed.

The example of “7.1100000494631581E+024” above suggests that variable dp is real(8), but is initialised with real(4) constant. If this is the basis of the claims, they appear to be wrong.
try “double precision dp /.711D25/”

It would be unfortunate if these wrong claims persisted.

1 Like

@IanMartinAjzenszmidt I merged all your threads into this one. When commenting here, just start a thread and then add comments to it, don’t start a new thread for every comment.

As pointed out by @JohnCampbell your example is using single precision constant. If you use the following code:

double precision :: dp = 0.711D25
real :: sp = 0.711
print *,dp
print *,sp
end program

Then it works correctly with both GFortran and LFortran on Apple M1 (ARM):

$ gfortran a.f90 && ./a.out 
   7.1099999999999997E+024
  0.711000025    
$ lfortran a.f90 
7.10999999999999974e+24
7.11000025e-01

I have run the same code on a Latitude E7440 as well. Here is the run.

(base) ian@ian-Latitude-E7440:~/Downloads$ nano gfortranx86dp.f
(base) ian@ian-Latitude-E7440:~/Downloads$ gfortran  gfortranx86dp.f
(base) ian@ian-Latitude-E7440:~/Downloads$ ./a.out
   7.1100000494631581E+024
  0.711000025    
(base) ian@ian-Latitude-E7440:~/Downloads$ cat  gfortranx86dp.f
          double precision dp /.711E25/
          real   sp  /.711/
          print *,dp
           print *,sp
           end program
   
(base) ian@ian-Latitude-E7440:~/Downloads$ lscpu
Architecture:            x86_64
  CPU op-mode(s):        32-bit, 64-bit
  Address sizes:         39 bits physical, 48 bits virtual
  Byte Order:            Little Endian
CPU(s):                  4
  On-line CPU(s) list:   0-3
Vendor ID:               GenuineIntel
  Model name:            Intel(R) Core(TM) i5-4300U CPU @ 1.90GHz
    CPU family:          6
    Model:               69
    Thread(s) per core:  2
    Core(s) per socket:  2
    Socket(s):           1
    Stepping:            1
    CPU max MHz:         2900.0000
    CPU min MHz:         800.0000
    BogoMIPS:            4988.33
    Flags:               fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mc
                         a cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss 
                         ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arc
                         h_perfmon pebs bts rep_good nopl xtopology nonstop_tsc 
                         cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vm
                         x smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1
                          sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsav
                         e avx f16c rdrand lahf_lm abm cpuid_fault epb invpcid_s
                         ingle pti ssbd ibrs ibpb stibp tpr_shadow vnmi flexprio
                         rity ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep
                          bmi2 erms invpcid xsaveopt dtherm ida arat pln pts md_
                         clear flush_l1d
Virtualization features: 
  Virtualisation:        VT-x
Caches (sum of all):     
  L1d:                   64 KiB (2 instances)
  L1i:                   64 KiB (2 instances)
  L2:                    512 KiB (2 instances)
  L3:                    3 MiB (1 instance)
NUMA:                    
  NUMA node(s):          1
  NUMA node0 CPU(s):     0-3
Vulnerabilities:         
  Gather data sampling:  Not affected
  Itlb multihit:         KVM: Mitigation: VMX disabled
  L1tf:                  Mitigation; PTE Inversion; VMX conditional cache flushe
                         s, SMT vulnerable
  Mds:                   Mitigation; Clear CPU buffers; SMT vulnerable
  Meltdown:              Mitigation; PTI
  Mmio stale data:       Unknown: No mitigations
  Retbleed:              Not affected
  Spec rstack overflow:  Not affected
  Spec store bypass:     Mitigation; Speculative Store Bypass disabled via prctl
  Spectre v1:            Mitigation; usercopy/swapgs barriers and __user pointer
                          sanitization
  Spectre v2:            Mitigation; Retpolines, IBPB conditional, IBRS_FW, STIB
                         P conditional, RSB filling, PBRSB-eIBRS Not affected
  Srbds:                 Mitigation; Microcode
  Tsx async abort:       Not affected
(base) ian@ian-Latitude-E7440:~/Downloads$ 

Can you do:

cat gfortranx86dp.f

The dp number must be equal to 0.711D25, not 0.711E25.

This thread only highlights the pedantic rules in the Fortran Standard regarding data constants, which are just not in the interest of minimising coding bugs for Fortran users.
If you read the same constant from a data file, you get the required result.

This delight was introduced in F90, when so many other positive improvements were made.
There are clearly Fortran users that struggle to understand this approach.

Why can’t a modern fortran optimising compiler accept the following line of code !
integer*8 :: i8 = 29000000000

It was also the case in most f77 and earlier compilers. The behavior was only specified clearly in f90, before that it was ambiguous. If you wanted consistent behavior prior to f90, you would also have specified the double precision constant in the source code.

In modern code, I think it is probably best to avoid DOUBLE PRECISION and D exponents entirely. They are still supported, for backwards compatiblity reasons, but modern code should be using the fortran KIND convention for both declarations and for literal constants.

Please Ron,

What was the intent of the following statements ?

double precision dp /.711E25/
integer*8 :: i8 = 29000000000

Why should we have a standard that clearly ignores this intent ?

I would actually like to have the following statement recognised:
integer*8 :: i8 = 2^32

(base) ian@ian-Latitude-E7440:~/Downloads$ ls *.f
dprecdemo.f gfortranx86dp.f q2.f secret_message.f testx86.f
(base) ian@ian-Latitude-E7440:~/Downloads$ gfortran dprecdemo.f
(base) ian@ian-Latitude-E7440:~/Downloads$ ./a.out
Double Precision Result: 1111111128.0000000
Real Result: 1.11111117E+09
(base) ian@ian-Latitude-E7440:~/Downloads$ cat dprecdemo.f
PROGRAM DoublePrecisionDemo

  • This program demonstrates the use of Double Precision and Real variables.

    DOUBLE PRECISION dVar1, dVar2, dResult
    REAL rVar1, rVar2, rResult
    
  • Initialize the variables
    dVar1 = 123456789.123456789
    dVar2 = 987654321.987654321

    rVar1 = 123456789.123456789
    rVar2 = 987654321.987654321
    
  • Perform arithmetic operations
    dResult = dVar1 + dVar2
    rResult = rVar1 + rVar2

  • Display the results
    PRINT *, 'Double Precision Result: ', dResult
    PRINT *, 'Real Result: ', rResult

    END PROGRAM
    

(base) ian@ian-Latitude-E7440:~/Downloads$ lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Address sizes: 39 bits physical, 48 bits virtual
Byte Order: Little Endian
CPU(s): 4
On-line CPU(s) list: 0-3
Vendor ID: GenuineIntel
Model name: Intel(R) Core™ i5-4300U CPU @ 1.90GHz
CPU family: 6
Model: 69
Thread(s) per core: 2
Core(s) per socket: 2
Socket(s): 1
Stepping: 1
CPU max MHz: 2900.0000
CPU min MHz: 800.0000
BogoMIPS: 4988.33
Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs
bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt
tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm cpuid_fault epb invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust b
mi1 avx2 smep bmi2 erms invpcid xsaveopt dtherm ida arat pln pts md_clear flush_l1d
Virtualization features:
Virtualisation: VT-x
Caches (sum of all):
L1d: 64 KiB (2 instances)
L1i: 64 KiB (2 instances)
L2: 512 KiB (2 instances)
L3: 3 MiB (1 instance)
NUMA:
NUMA node(s): 1
NUMA node0 CPU(s): 0-3
Vulnerabilities:
Gather data sampling: Not affected
Itlb multihit: KVM: Mitigation: VMX disabled
L1tf: Mitigation; PTE Inversion; VMX conditional cache flushes, SMT vulnerable
Mds: Mitigation; Clear CPU buffers; SMT vulnerable
Meltdown: Mitigation; PTI
Mmio stale data: Unknown: No mitigations
Retbleed: Not affected
Spec rstack overflow: Not affected
Spec store bypass: Mitigation; Speculative Store Bypass disabled via prctl
Spectre v1: Mitigation; usercopy/swapgs barriers and __user pointer sanitization
Spectre v2: Mitigation; Retpolines, IBPB conditional, IBRS_FW, STIBP conditional, RSB filling, PBRSB-eIBRS Not affected
Srbds: Mitigation; Microcode
Tsx async abort: Not affected
(base) ian@ian-Latitude-E7440:~/Downloads$

The same second code run on an ARM Cortex -M53 instead of genuine intel. userland@localhost:~ nano testarm.f userland@localhost:~ gfortran testarm.f -ffree-form
userland@localhost:~ ./a.out Double Precision Result: 2.5042347108182738E-312 Real Result: 1.11111117E+09 userland@localhost:~ cat testarm.f
DOUBLE PRECISION dVar1, dVar2, dResult
REAL rVar1, rVar2, rResult

!Initialize the variables
dVar1 = 123456789.123456789
dVar2 = 987654321.987654321

rVar1 = 123456789.123456789
rVar2 = 987654321.987654321

!Perform arithmetic operations
Result = dVar1 + dVar2
rResult = rVar1 + rVar2

!Display the results
PRINT *, 'Double Precision Result: ', dResult
PRINT *, 'Real Result: ', rResult

END PROGRAM

userland@localhost:~ lscpu Architecture: aarch64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian CPU(s): 4 On-line CPU(s) list: 0-3 Vendor ID: ARM Model name: Cortex-A53 Model: 4 Thread(s) per core: 1 Core(s) per cluster: 4 Socket(s): - Cluster(s): 1 Stepping: r0p4 CPU max MHz: 2001.0000 CPU min MHz: 850.0000 BogoMIPS: 26.00 Flags: fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid Vulnerabilities: Itlb multihit: Not affected L1tf: Not affected Mds: Not affected Meltdown: Not affected Spec store bypass: Not affected Spectre v1: Mitigation; __user pointer sanitization Spectre v2: Not affected Srbds: Not affected Tsx async abort: Not affected userland@localhost:~

That is exactly the problem. Without looking at how these values are used, the intent is unclear, both to a human looking at the code and also for a compiler looking at the code. Maybe the programmer intended to have 64-bit accuracy, maybe he intended to have 32-bit accuracy. But one thing is almost certain about this situation, the programmer does not want different behavior on different processors. The programmer almost certainly wants the code to do the same thing on different compilers, or on the same compiler on different days, or with new and old versions of a compiler, or on different hardware that supports the same floating point formats.

If I ask you what is the intent of the following code:

use, intrinsic:: iso_fortran_env, only: sp=>real32, dp=>real64
real(sp) :: s = 0.1234_sp
real(dp) :: ds = 0.1234_sp, dd = 0.1234_dp

then I’m pretty sure we will both agree. And when a compiler looks at that code, it will agree with us. My point is that with the fortran KIND system, introduced in f90, this is now a solved problem, the code can be clearly written in an almost self-documenting way. There is no longer any need to argue about what is or isn’t intended by some ambiguous declarations or assignments.

1 Like

Following is a run listing including suggested edits:
(base) ian@ian-Latitude-E7440:~/Downloads$ cat testx86.f
use, intrinsic:: iso_fortran_env, only: sp=>real32, dp=>real64
real(sp) :: s = 0.1234_sp
real(dp) :: ds = 0.1234_sp, dd = 0.1234_dp
!double precision :: dp = 0.711D25
!real :: sp = 0.711
print *,dp
print *,sp
print *,s
print *,ds
print *,dd
end program
(base) ian@ian-Latitude-E7440:~/Downloads$ gfortran testx86.f -o testx86
(base) ian@ian-Latitude-E7440:~/Downloads$ ./testx86
8
4
0.123400003
0.12340000271797180
0.12340000000000000
(base) ian@ian-Latitude-E7440:~/Downloads$ ^C
(base) ian@ian-Latitude-E7440:~/Downloads$ lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Address sizes: 39 bits physical, 48 bits virtual
Byte Order: Little Endian
CPU(s): 4
On-line CPU(s) list: 0-3
Vendor ID: GenuineIntel
Model name: Intel(R) Core™ i5-4300U CPU @ 1.90GHz
CPU family: 6
Model: 69
Thread(s) per core: 2
Core(s) per socket: 2
Socket(s): 1
Stepping: 1
CPU max MHz: 2900.0000
CPU min MHz: 800.0000
BogoMIPS: 4988.39
Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca c
mov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm
pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon
pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfm
perf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 s
sse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic mov
be popcnt tsc_deadline_timer aes xsave avx f16c rdrand lah
f_lm abm cpuid_fault epb invpcid_single pti ssbd ibrs ibpb
stibp tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsba
se tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt dt
herm ida arat pln pts md_clear flush_l1d
Virtualization features:
Virtualisation: VT-x
Caches (sum of all):
L1d: 64 KiB (2 instances)
L1i: 64 KiB (2 instances)
L2: 512 KiB (2 instances)
L3: 3 MiB (1 instance)
NUMA:
NUMA node(s): 1
NUMA node0 CPU(s): 0-3
Vulnerabilities:
Gather data sampling: Not affected
Itlb multihit: KVM: Mitigation: VMX disabled
L1tf: Mitigation; PTE Inversion; VMX conditional cache flushes,
SMT vulnerable
Mds: Mitigation; Clear CPU buffers; SMT vulnerable
Meltdown: Mitigation; PTI
Mmio stale data: Unknown: No mitigations
Retbleed: Not affected
Spec rstack overflow: Not affected
Spec store bypass: Mitigation; Speculative Store Bypass disabled via prctl
Spectre v1: Mitigation; usercopy/swapgs barriers and __user pointer sa
nitization
Spectre v2: Mitigation; Retpolines, IBPB conditional, IBRS_FW, STIBP c
onditional, RSB filling, PBRSB-eIBRS Not affected
Srbds: Mitigation; Microcode
Tsx async abort: Not affected
(base) ian@ian-Latitude-E7440:~/Downloads$

The last line should have been
dResult = dVar1 + dVar2
As it is, your program prints out dResult without assigning the variable a value.

Because of this error, apparent misunderstandings on your part of the consequences of assigning an expression of type REAL to a variable of type DOUBLE PRECISION, and undue haste in drawing conclusions, this thread is rather misleading and pointless.

3 Likes

Only that real64 doesn’t guarantee double precision accuracy, but only an exact storage size…

1 Like

The first and last floating point values are exactly the same internally. This is because every floating point real can be represented exactly as a double precision. One could see this by printing the variables with a Z or B format. The difference you see here is an artifact of the list-directed i/o conventions. Namely, the real value is printed with fewer digits, so it is displayed as the rounded version of the last value. If instead of list-directed formatting you use a specific format for both variables, say something like f20.17, then you should see the same digits printed for the first and last values.

So in addition to the difference in the output due to the different precisions of the constants, there is also this quirk related to list-directed i/o.

This is correct. In practice, you usually get a little more than twice the precision. For example, 24-bits for real and 53-bits for double precision (counting the hidden bits).

We have had this discussion before, and the answers to these questions have been giiven.

But is there any compiler where real64 /= double precision?

Beginners are confused by selected_real_kind, and all these subtle details about how to use real numbers in Fortran. It’s just way too complicated. People come from other languages where there is only one kind of floating point number (or where double is the default) and make these errors every time.