Formatting number to a specific number of digits in Fortran - fortran

I am trying to format a number to a certain number of digits (7, actually) like the following:
215.5745
6.569674
16.42418
According to this page ("F, D, E, and G Format Codes"), the possible format code would be:
[n]F[+][-][w][.d]
The variable w specifies the number of digits to be transferred
which appears to be exactly what I am looking for.
I understand that the d parameter should not be provided as this would cause the number of decimal positions to be fixed, and could be omitted since:
d is an optional width specification
However, gfortran refuses to compile when a format code like "F7" is provided.
Error: Period required in format specifier in format string at (1)

The linked page appears to be over-simplified or straight-out incorrect. The period is indeed obligatory. The number the period must be non-negative (positive or zero).
I do not think you can do exactly what you require using these format descriptors. Instead, I would first print the number into a string and then only print the first 8 digits of that string - but it is not accurate because it just cuts the remaining digits instead of rounding them.
If you need to be precise, you will have to count the digits before the comma and set-up the format string manually. There won't be a universal format string to use as far as I am aware.
double precision :: a(3) = [215.5745d0,6.569674d0,16.42418d0]
integer :: digs(3)
character(20) :: form
digs = log10(a) + 1
do i = 1, 3
write(form,'(a,i0,a)') "(f8.", max(min(7-digs(i),7),0), ")"
print form, a(i)
end do
end

I think the g0.7 formatting spec should give you exactly what you want.
here is a small test code for formatting.
print '(1x,a24,1x,a24,1x,a24)', "g24.7", "e24.7", "f24.7"
do i=1, n
print '(1x,g24.7,1x,e24.7,1x,f24.7)', a64(i), a64(i), a64(i)
end do
and the sample output
As you can see the gw.7 type of format retains a certain number of significant digits which is what your example shows. But if this is not possible, then it reverts to scientific formatting.
g24.7 e24.7 f24.7
0.3710828E-02 0.3710828E-02 0.0037108
8.318075 0.8318075E+01 8.3180745
33.02631 0.3302631E+02 33.0263091
133.7840 0.1337840E+03 133.7839844
152.5674 0.1525674E+03 152.5673802
274.5596 0.2745596E+03 274.5595695
317.6714 0.3176714E+03 317.6713521
452.4193 0.4524193E+03 452.4193376
646.8024 0.6468024E+03 646.8024139
1117.582 0.1117582E+04 1117.5816049
1384.222 0.1384222E+04 1384.2222737
5529.837 0.5529837E+04 5529.8374344
6555.235 0.6555235E+04 6555.2346832
24251.70 0.2425170E+05 24251.6980498
27283.39 0.2728339E+05 27283.3873660
167907.9 0.1679079E+06 167907.8873403
270053.5 0.2700535E+06 270053.4615256
317435.1 0.3174351E+06 317435.0603955
586812.5 0.5868125E+06 586812.5125615
3538679. 0.3538679E+07 3538679.3597321

Related

How to convert text field with formatted currency to numeric field type in Postgres?

I have a table that has a text field which has formatted strings that represent money.
For example, it will have values like this, but also have "bad" invalid data as well
$5.55
$100050.44
over 10,000
$550
my money
570.00
I want to convert this to a numeric field but maintain the actual numbers that can be retained, and for any that can't , convert to null.
I was using this function originally which did convert clean numbers (numbers that didn't have any formatting). The issue was that it would not convert $5.55 as an example and set this to null.
CREATE OR REPLACE FUNCTION public.cast_text_to_numeric(
v_input text)
RETURNS numeric
LANGUAGE 'plpgsql'
COST 100
VOLATILE
AS $BODY$
declare v_output numeric default null;
begin
begin
v_output := v_input::numeric;
exception when others then return null;
end;
return v_output;
end;
$BODY$;
I then created a simple update statement which removes the all non digit characters, but keeps the period.
update public.numbertesting set field_1=regexp_replace(field_1,'[^\w.]','','g')
and if I run this statement, it correctly converts the text data to numeric and maintains the number:
alter table public.numbertesting
alter column field_1 type numeric
using field_1::numeric
But I need to use the function in order to properly discard any bad data and set those values to null.
Even after I run the clean up to set the text value to say 5.55
my "cast_text_to_numeric" function STILL sets this to null ? I don't understand why this sets it to null, but the above statement correctly converts it to a proper number.
How can I fix my cast_text_to_numeric function to properly convert values such as 5.55 , etc?
I'm ok with disgarding (setting to NULL) any values that don't end up with numbers and a period. The regular expression will strip out all other characters... and if there happens to be two numbers in the text field, with the script, they would be combined into one (spaces are removed) and I'm good with that.
In the example of data above, after conversion, the end result in numeric field would be:
5.55
100050.44
null
550
null
570.00
FYI, I am on Postgres 11 right now

How to make a file with i number of rows

I'm using this code below to generate a file with i number (i=200 for instance) of rows, but the first and the second rows are fixed and I just want to create another i rows using a random extraction in a sphere of unitary radius in Fortran. Each row should start with m1 and then 6 random numbers between a range [0,1].
program InputGen
implicit none
integer :: i,n,j
character(len=500) :: firstLine, secondLine
real *8 :: m1
real:: r(10)
m1=1.d0
firstLine='3 5 1.d-4 1.d5 0.e-3 0.0 1. 0.1 0.e0 1'
secondLine='4.d6 0. 0. 0. 0. 0. 0. '
call random_seed()
call random_number(r)
open(unit=100, file='INPUT.TXT',Action='write', Status='replace')
write(100,'(A)') trim(firstLine)
write(100,'(A)') trim(secondLine)
do i=1,200
write(100,'(A)') '',m1,' ',(r(n),n=1,10),
' ',(r(n),n=1,10),'0.0',&
' ',(r(n),n=1,10),&
' ',(r(n),n=1,10),'0.0'
end do
write(*,*) 'Input file generated.'
write(*,*) 'Press Enter to exit...'
read(*,*)
end program InputGen
The first and second lines create perfectly, but the other rows in the loop not.
You did not tell us what is wrong (how the problem exhibits), but I suspect the format is incorrect. You are just specifying (A), but you have a mixed output list with strings and numbers.
You can just use a simple general format like (*(g0)) that will apply the generic g0 format to all items in the input list. You will want to add some manual spaces (although you already have some) in the input list to avoid joining two unrelated output items on the line.
Or you can just follow the input list and add a specific format for each item, like (a,1x,f12.6,a,10(f12.6,1x),a ... and so on. Adjust as needed, especially the spaces (either keep them as ' ' with the a descriptor, or use the 1x descriptor.
You are also currently writing the same r all the time. You should generate more numbers and re-generate them an each loop iteration
real:: r(40)
do i=1,200
call random_number(r)
write(100,'(*(g0))') '',m1,' ',r(1:10),
' ',r(11:20),'0.0',&
' ',r(21:30),&
' ',r(31:40),'0.0'
end do
This does not do anything with points in a sphere or anything similar, this just prints random numbers. I hope that is clear.

FORTRAN code with ascii data

I have a data in ASCII format (.txt file) in which date is given in a column in format yearmonthday (i.e.19900601). I want to separate this column into three columns with year, month and date in each column. Can anyone tell how to do this in Fortran? My data file and code is as following:
datum nied
19480501 -1
19480502 -1
19480503 2
19480504 -1
19480505 2
19480506 -1
19480507 -1
19480508 -1
19480509 -1
19480510 -1
19480511 -1
19480512 2
. .
. .
Code:
program ascii_read
!real(kind=8):: rain(np)
real,allocatable:: rain(:)
integer::np=15739
!integer(kind=8)::day(np)
integer,allocatable::day(:)
character(len = 80)::firstline
integer::i,j
integer,allocatable:: year(:)
allocate (year(np-1))
allocate (rain(np))
allocate (day(np))
open(1243,file="11700.text",status="unknown")
open(12,file="11700_output.text",status="unknown")
read(1243,*)firstline
do i=2,np
read(1243,1111)day(i),rain(i)
end do
1111 Format(i6,F5.2)
write(*,*)day
do j = 1,np-1
year(j)=day(j)
end do
write(*,fmt='(i4)')year
1 format(I4)
!write(*,*)year
return
stop
end program
This gives only year separate in a column,NOT month and day. Any idea how to separate month and day from this data file?
you can use a formatted read to explicitly pull out each of the fields:
integer year,month,day,rain
...
read(1234,'(i4,i2,i2,i3)')year,month,day,rain
In your code you use i6 so day(i) holds things like '194805', then rain(i) is read from the remainder of the line (ie. the last two digits of the "date" integer a space and another integer). I don't know what the f5.2 format does with that but it can't be what you want)
You have to analyse the relationship between your input and the output that you want and then implement the relationship; that is how programming works. You have to first know a method to solve the problem by yourself and then teach the computer how to do it.
For this problem, you can simply see that the first 4 digits represent the year, the next two the month and the last 2 the date. To get the first 4, you divide the full number by 10000, it simply reject the last 4 (month and day). You use the modulo operation to get the last four. And do the same to extract the month from the last two.
Define new array variables month and date and allocate them to the same size as day, also add a new integer variable tmp and change your second loop to this:
do j = 1,np-1
year(j)=day(j)/10000
tmp = mod(day(j), 10000)
month(j) = tmp/100
date(j) = mod(tmp,100)
end do
I will also advise you to use free formatting for reading. You can use fixed format for writing to align data and make it easy to visualize.
Go for modern programming when you start. Litteral numbers are not a good idea in a code, so use named constants for file ids. Make sure that you close files when you do not need them anymore. When you are openning file for reading, use status='old', you want the file to be there or you want you program to stop with an appropriate message. When you are using format, use the format parameter of read and write instead of format statement, for example with the name arg fmt as you did at some places. This make it easy to debug. So your program could look like this.
program ascii_read
!real(kind=8):: rain(np)
integer, parameter :: inputId = 1243
integer, parameter :: outputId = 12
real,allocatable,dimension(:):: rain
integer::np=12
!integer(kind=8)::day(np)
character(len = 80)::firstline
integer::i,j, tmp
integer,allocatable,dimension(:):: day, year, month, date
allocate ( year(np-1), rain(np), day(np), month(np), date(np) )
open(inputId,file="11700.text",status="old")
open(outputId,file="11700_output.text",status="unknown")
read(inputId,*)firstline
do i=2,np
read(inputId,*)day(i),rain(i)
end do
close(inputId)
write(*,*) day
do j = 1,np-1
year(j)=day(j)/10000
tmp = mod(day(j), 10000)
month(j) = tmp/100
date(j) = mod(tmp,100)
! just to see what we get.
write(*, *) day(j), year(j), month(j), date(j)
end do
!write(*,fmt='(i4)')year
!1 format(I4)
!write(*,*)year
return
stop
end program
Thank to IanH for the comment, latest version of fortran included a newunit option that takes care of the IO unit number for programmers. This frees you from defining named constant for unit number. If you do not work with a latest version (Some companies do not upgrade often), there is one ready for use in fortranwiki.
A "hybrid" approach of the other two answers is to first read in the data into a buffer and then split it into integers
character(50) buf
integer year, month, day, rain
read( 10, * ) buf, rain
read( buf, "(i4,i2,i2)" ) year, month, day
! or equivalently
! read( buf(1:4), * ) year
! read( buf(5:6), * ) month
! read( buf(7:8), * ) day
Here, list-directed I/O is used to skip possible spaces before the first column, while integers are extracted based on widths. Also, comment lines starting with "#" (if any) can be skipped by inserting if ( buf(1:1) == "#" ) cycle after the first read statement, for example.

Converting a string of numbers to hex and back to dec pandas python

I currently have a string of values which I retrieved after filtering through data from a csv file. ultimately I had to do some filtering of the data but I have the same numbers as a list, dataframe, or array. I just need to take the numbers in the string and convert them to hex and then take the first 8 numbers of the hex and convert that to dec for each element in the string. Lastly I also need to convert the last 8 of the same hex and then to dec as well for each value in the string.
I cannot provide a snippet because it is sensitive data, but here is an example.
I basically have something like this
>>> list_A
[52894036, 78893201, 45790373]
If I convert it to a dataframe and call df.dtypes, it says dtype: object and I can convert the values of Column A to bool, int, or string, but the dtype is always an object.
It does not matter whether it is a function, or just a simple loop. I have been trying many methods and am unable to attain the results I need. But ultimately the data is taken from different csv files and will never be the same values or list size.
Pandas is designed to work primarily with integers and floats, with no particular facilities for hexadecimal that I know of, but you can use apply to access standard python conversion functions like hex and int:
df=pd.DataFrame({ 'a':[52894036999, 78893201999, 45790373999] })
df['b'] = df['a'].apply( hex )
df['c'] = df['b'].apply( int, base=0 )
Results:
a b c
0 52894036999 0xc50baf407 52894036999
1 78893201999 0x125e66ba4f 78893201999
2 45790373999 0xaa951a86f 45790373999
Note that this answer is for Python 3. For Python 2 you may need to strip off the trailing "L" in column "b" with str[:-1].

How to convert big-number to varchar in Teradata

I have a float-type-field which contains big number (more than 20 digits). Then I want to convert it to varchar, without rounding. I tried several queries, but nothing.
Here is for example :
SELECT CAST(A AS VARCHAR(25)) AS B
, TRIM(TRAILING '.' FROM CAST(CAST(A AS DECIMAL(25)) AS VARCHAR(25))) AS C
, TRIM(LEADING '0' FROM CAST(CAST(A AS FORMAT '9(25)') AS VARCHAR(25))) AS D
FROM ( SELECT CAST(79999999999999999999.000 AS FLOAT) A ) t0
And the result
B : 8.00000000000000E 019
C : 80000000000000000000
D : 80000000000000000000
I expect to get '79999999999999999999'
Any advises are appreciated.
When you run SELECT CAST(79999999999999999999.000 AS FLOAT) you get 80.000.000.000.000.000.000,000 as result. The precision of a FLOAT is 15 to 16 digits, your input has 20 significant digits, so it's already rounded during the typecast.
As you can't store numbers like this in a Teradata FLOAT the question is: Where's this "float-type-field" coming from? Oracle?
Is there a know maximum n for fractional digits? Then CASTing as DECIMAL(38,n) might be ok.
What's your TD release? 14 introduced the NUMBER datatype to easy porting from Oracle.
NUMBER might be used as a "float-type-field" with approx. 38 digits.