Output is '*' when writing a real to a string - fortran

I have a real CURRENTTIME I want to convert to a string named TIMEDIR. As TIMEDIR has to change size it is allocatable. As far as I could find out, the allocation works fine. Also, I checked that CURRENTTIME has a value.
ALLOCATE(CHARACTER(LEN=1)::TIMEDIR)
WRITE(TIMEDIR, '(F1.0)') CURRENTTIME
But
WRITE(*,*) TIMEDIR
outputs *, where it should be 0 (CURRENTTIME is 0.0000000). I have no clue what the problem is.

You're writing the output as a floating point number. Floating point numbers always have a decimal point or an exponent to differentiate them from integers. Thus the narrowest output of a float possible is 0., i.e. 2 characters, and a format of F1.0 will always result in a "*" being printed as the field width is insufficient for what is being written.

Ian Bush's answer says what you need to know: output for a real value using the F edit descriptor requires a field width of at least 2. I'll elaborate a bit on some other aspects.
You mention
As TIMEDIR has to change size it is allocatable
but in the code fragment we see
WRITE(TIMEDIR, '(F1.0)') CURRENTTIME
This suggests a little misunderstanding. [It may be that there's no confusion, but I'll labour the point for the benefit of any other reader coming to the question.]
When an output format looks like Fw.d for w greater than zero the width of the output field is always w. This "always" means: whatever the value of the corresponding variable, the effect of the write statement above on TIMEDIR is to have a single character non-blank.
Now, as in that other answer, 2 is the minimum field width for output of a real value[1]. As with all other numeric output formatting, if the field isn't wide enough for the corresponding value the field consists entirely of *s. F1.0 will always result in output *. If you want output 0. (or 0,)[2] you'll need F2.0.
Coming back to the "varying size of TIMEDIR", output format F2.0 is (possibly) sufficient for non-negative values of CURRENTTIME less than 10, but for negative values or values not less than 10 it isn't. It may well be that this is where F0.d comes in. It's only with this form of the F edit descriptor that the width of the field depends on the output value. That's probably an answer to another question, though.
Finally, as you mention
I have to find out how to make "0" out of "0."
I'll point out that you're looking at having to do some additional logic, such as mentioned elsewhere.
1 And 2 may not be sufficient, even for a zero value: print '(SP,F2.0)', 0.
2 The choice of 0. and 0, depends on the decimal mode: print '(DC,F2.0,DP,F2.0)', 0., 0.

Related

Identify the value with highest number of decimal values

I have a range of values and I want to count the decimal points of all values in the range and display the max count. the formula should exclude the zeroes at the end(not count ending zeroes in the decimal points).
for example, in the above sample, in the whole range the max of count of decimal places is 4 excluding the ending zeroes. so the answer is 4 to be displayed in cell D2
I tried doing regex, but do not know how do I do it for a whole range of values.
Please help!
try:
=INDEX(MAX(LEN(IFERROR(REGEXEXTRACT(TO_TEXT(A2:C4), "(\..+)")*1))-2))
Player0's solution is a good start, but uses TO_TEXT which seems to rely on the formatting of your cells.
If you want to safely compute the number of decimal places, use the TEXT function instead.
TEXT(number, format) requires a format whose max. number of decimal places has to be specified. There is no way around this, because formulas like =1/3 can have infinitely many decimal places.
Therefore, first decide on the max, precision for your use-case (here we use 8). Then use below function which works independently from your document's formatting and language:
=INDEX(MAX(
LEN(REGEXEXTRACT(
TEXT(ABS(A2:C4); "."&REPT("#";8));
"[,.].*$"
))-1
))
We subtract -1 since LEN(REGEXEXTRACT()) also counts the decimal separator (. for english, , for many others) .
Everything after the 8th decimal place is ignored. If all your numbers are something like 123.00000000987 the computed max. is 0. If you prefer it to be 8 instead, then add ROUNDUP( ; 8):
=INDEX(MAX(
LEN(REGEXEXTRACT(
TEXT(ROUNDUP(ABS(A2:C4);8); "."&REPT("#";8));
"[,.].*$"
))-1
))

Understand %03.3u in printf format specification

I am using printf to output contents. Now I see the format specification as "%03.3u" in another person's code, as per my understanding the "03" before the dot already specifies the width of the output as 3 digits, and padding with zeros if there are not 3, while the "3" after the dot also specifies that there should be 3 digits output. Therefore, it seems "03" before the dot and "3" after the dot is duplicated.
I make the following tests:
char l[50];
sprintf(l, "%03.3u", 5);
sprintf(l, "%03u", 5);
sprintf(l, "%.3u", 5);
And confirm the output is always 005. So why someone else should use "%03.3u" instead of "%03u" or "%.3u"?
The output will be the same for the particular values you have used. The number before the . is the minimum field width while the number after (for the u conversion specifier, at least) it is the minimum number of digits to output. You can see the difference between the two with something like:
printf("%3.2u\n", 7)
which gives you space07 - minimum two digits output and minimum three characters wide.
However, the fact that you have the numbers the same means that you'll get three digits minimum in a field at least three characters wide. Even if you had used %03.2u (different minimums), the presence of that 0 means to left-pad with 0 rather than space, so you'd still see 005.
Bottom line is, to get the full three digits, you can use the 0 zero-pad modifier or the minimum digit count modifier but you don't need both.
However, since having both doesn't have any adverse effects beyond forcing people to question the sanity of those that wrote it :-), it's functionally okay.
The 03 is the field width with zero-padding. This means that a minimum of 3 characters are to be output, and if there were fewer than three, left-pad with zeroes.
The second 3 is the minimum number of digits to output.
When both of these are specified, the precision will be applied, and if the result is narrower than the minimum field width, then the output will be padded. For exampleprintf("q%6.3u", 5) will produce q 005 . (I use the q because stackoverflow formatting eats the spaces otherwise).
If you're printing an unsigned integer and you didn't use the sign flag, then the number of digits is the same as the field width (since the only output is digits). %03u, %.3u and %03.3u all have the same effect.
I guess the person wrote %03.3u since they did not properly understand the meaning of these things so they guessed something, it worked, and they decided to not make any further changes.
If you print a sign character then the field width differs from the digit count, e.g. you could experiment with %+3u versus %+.3u. Or if you use %d and print a negative number.

Controlling newlines when writing out arrays in Fortran

So I have some code that does essentially this:
REAL, DIMENSION(31) :: month_data
INTEGER :: no_days
no_days = get_no_days()
month_data = [fill array with some values]
WRITE(1000,*) (month_data(d), d=1,no_days)
So I have an array with values for each month, in a loop I fill the array with a certain number of values based on how many days there are in that month, then write out the results into a file.
It took me quite some time to wrap my head around the whole 'write out an array in one go' aspect of WRITE, but this seems to work.
However this way, it writes out the numbers in the array like this (example for January, so 31 values):
0.00000 10.0000 20.0000 30.0000 40.0000 50.0000 60.0000
70.0000 80.0000 90.0000 100.000 110.000 120.000 130.000
140.000 150.000 160.000 170.000 180.000 190.000 200.000
210.000 220.000 230.000 240.000 250.000 260.000 270.000
280.000 290.000 300.000
So it prefixes a lot of spaces (presumably to make columns line up even when there are larger values in the array), and it wraps lines to make it not exceed a certain width (I think 128 chars? not sure).
I don't really mind the extra spaces (although they inflate my file sizes considerably, so it would be nice to fix that too...) but the breaking-up-lines screws up my other tooling. I've tried reading several Fortran manuals, but while some of the mention 'output formatting', I have yet to find one that mentions newlines or columns.
So, how do I control how arrays are written out when using the syntax above in Fortran?
(also, while we're at it, how do I control the nr of decimal digits? I know these are all integer values so I'd like to leave out any decimals all together, but I can't change the data type to INTEGER in my code because of reasons).
You probably want something similar to
WRITE(1000,'(31(F6.0,1X))') (month_data(d), d=1,no_days)
Explanation:
The use of * as the format specification is called list directed I/O: it is easy to code, but you are giving away all control over the format to the processor. In order to control the format you need to provide explicit formatting, via a label to a FORMAT statement or via a character variable.
Use the F edit descriptor for real variables in decimal form. Their syntax is Fw.d, where w is the width of the field and d is the number of decimal places, including the decimal sign. F6.0 therefore means a field of 6 characters of width with no decimal places.
Spaces can be added with the X control edit descriptor.
Repetitions of edit descriptors can be indicated with the number of repetitions before a symbol.
Groups can be created with (...), and they can be repeated if preceded by a number of repetitions.
No more items are printed beyond the last provided variable, even if the format specifies how to print more items than the ones actually provided - so you can ask for 31 repetitions even if for some months you will only print data for 30 or 28 days.
Besides,
New lines could be added with the / control edit descriptor; e.g., if you wanted to print the data with 10 values per row, you could do
WRITE(1000,'(4(10(F6.0,:,1X),/))') (month_data(d), d=1,no_days)
Note the : control edit descriptor in this second example: it indicates that, if there are no more items to print, nothing else should be printed - not even spaces corresponding to control edit descriptors such as X or /. While it could have been used in the previous example, it is more relevant here, in order to ensure that, if no_days is a multiple of 10, there isn't an empty line after the 3 rows of data.
If you want to completely remove the decimal symbol, you would need to rather print the nearest integers using the nint intrinsic and the Iw (integer) descriptor:
WRITE(1000,'(31(I6,1X))') (nint(month_data(d)), d=1,no_days)

Incorrect conversion when decimal point embedded in VT_BSTR and German locale used

I have a piece of code(c++) that is writing some floating point values to excel like this:
...
values[ position ].bstrVal = formattedValue;
values[ position ].vt = VT_BSTR;
...
as you can see those floating point values are stored in the form of string and the decimal point is formatted in different ways, for example:
"110.000000", "20.11" etc. (this example is for English locale)
Now it works perfectly when English locale is used. However when I switch to German locale in the Control Panel the decimal point is changed to "," (and that's fine) but after passing those localized strings to Excel they are not correctly converted. For example in case of writing "110,000000" I'm getting 100 millions in excel. Other values like "20,11" stay as a text.
The only way to fix this is to overwrite the decimal point with "." in my program before writing to Excel. Any ideas why the conversion is not locale-aware when using VT_BSTR?
I should also add that I tried to switch the locale in my program from default one to German - still no luck.
Thank you in advance
It is never a good idea to let Excel guess at the value type. Do not use VT_BSTR, a currency value should be of variant type VT_CY. Assign the cyVal member with the value. It is an 8 byte integer value (int64 member of type LONGLONG), the currency amount multiplied by 10,000. Ten thousand :)

Error when reading in float in Fortran

This should be quite simple, but I can't manage to read in a floating point number in Fortran. My program test.f looks like this:
PROGRAM TEST
open(UNIT=1,FILE='test.inp')
read(1,'(f3.0)')line
STOP
END
The input file test.inp simply contains a single float: 1.2
Now the compiling of my testfile goes fine, but when I run it I get an error:
At line 4 of file test.f (unit = 1, file = 'test.inp')
Fortran runtime error: Expected REAL for item 1 in formatted transfer, got INTEGER
(f3.0)
^
I've tried different modifications of the code and also googling for the error message, but with no result. Any help would be greatly appreciated!
Regards,
Frank
Your variable line is implicitly defined as integer. This doesn't work with thef edit descriptor. If you want to read an integer use i edit descriptor (i3 for example). Otherwise declare line as real to math the "f" descriptor.
Note beside: the .0 is not a problem, because if Fortran gets a number with decimal point the .0 part in the descriptor is ignored. It is only used when an number without a decimal is entered and then it uses the number behind the decimal point in the desciptor to add a decimal point into the right place. For with F8.5, 123456789 is read as 123.45678. More ont this here http://software.intel.com/sites/products/documentation/hpc/compilerpro/en-us/fortran/lin/compiler_f/lref_for/source_files/pghredf.htm .
In your read statement
read(1,'(f3.0)')line
the f3.0 tells tour program to read 3 digits with 0 digits after the decimal (this is what the n.m syntax means). So I presume that the program is just reading 1 from the file (not 1.2), which is an integer. Try replacing that line with something like
read(1,'(f3.1)')line
although, if the number in your file is likely to change and be larger than 9.9 or have more than one decimal place you should increase the field width to something larger than 3.
See the documentation of the read intrinsic and for data edit descriptors for more information on reading and writing in Fortran.
Edit: the format specifier, the second argument in quotes in your read statment, has the form fw.d, where f indicates that the data to read is a floating point number, w is the width of the field including all blanks and decimal points and d specifies the number of digits to the right of the decimal point.
I would suggest reading/writing list formatted data, unless you have a very strong reason to do otherwise. assuming that you're reading in from a file with just a single float or integer in a single line, like this
123.45
11
42
then this should do the reading
real*8 :: x,y,z
open(1,file=filename)
read(1,*)x
read(1,*)y
read(1,*)z
close(1)