Related
I've inherited some Fortran code that I'm trying to make sense of. It uses REAL variables in many places that, I think, it shouldn't - but maybe I'm misunderstanding how this works in Fortran (as compared to C++ which I'm much more familiar with), hence this question.
So the variables in question are essentially 'categorical values', 'factors' or 'enums' depending on how you look at it/want to call it. They are of data type REAL and can only take on a finite number of pre-determined, integer values. So say variable a can only be of value 1, 2 or 3. These values are read in from external files; in these external files, they are represented as integers, so it's not a case of 'rounding issues in external data sources' or something.
However in the code, it never does a straight comparison, always a greater than/lower than check. So, instead of doing
if (a == 1) then
it does
if (a > 0.9 .and. a < 1.1) then
You can imagine that this gets very confusing/tiresome to read, especially when it needs to check if a value is one of multiple categories.
So I think this is a case where someone at some point heard 'never compare REAL values' (because of the nature of the finite precision of storing floating point values, this same problem exists in every programming language), but then didn't really understand when that applies (I guess the first error is that categorical values should have been represented as integer values but that situation is what it is for now).
OTOH maybe I'm just misunderstanding how REAL and INTEGER values are represented and work in Fortran? Could there ever be a case where
b = 1.5
a = REAL(INT(b))
if (a > 0.9 .and. a < 1.1) then
would make sense?
ONLY in the case you're not performing any operation with the real values (just assigning a value and comparing equality with the same literal you assigned, with same kind parameter), you won't need tolerance.
The thing is, for a real variable a such as:
Real a
a = 2
You can be sure that
a == 2
Will be always .true. . But e.g., for another real value represented by b:
a / b * b == 2
(Or any other operation) is not guaranteed to be .true.
If for some reason the variable has to stay REAL then you may use
the intrinsic Fortran function NINT (nearest integer) in the comparisons:
if( nint(a) == 1 ) then
....
Alright so I am trying to truncate actual values from a double with a given number of digits precision (total digits before and after, or without, decimal), not just output them, not just round them. The only built in functions I found for this truncates all decimals, or rounds to given decimal precision.
Other solutions I have found online, can only do it when you know the number of digits before the decimal, or the entire number.
This solution should be dynamic enough to handle any number. I whipped up some code that does the trick below, however I can't shake the feeling there is a better way to do it. Does anyone know of something more elegant? Maybe a built in function that I don't know about?
I should mention the reason for this. There are 3 different sources of observed values. All 3 of these sources agree to some level in precision. Such as below, they all agree within 10 digits.
4659.96751751236
4659.96751721355
4659.96751764253
However I need to only pull from 1 of the sources. So the best approach, is to only use up to the precision all 3 sources agree on. So its not like I am manipulating numbers and then need to truncate precision, they are observed values. The desired result is
4659.967517
double truncate(double num, int digits)
{
// check valid digits
if (digits < 0)
return num;
// create string stream for full precision (string conversion rounds at 10)
ostringstream numO;
// read in number to stream, at 17+ precision things get wonky
numO << setprecision(16) << num;
// convert to string, for character manipulation
string numS = numO.str();
// check if we have a decimal
int decimalIndex = numS.find('.');
// if we have a decimal, erase it for now, logging its position
if(decimalIndex != -1)
numS.erase(decimalIndex, 1);
// make sure our target precision is not higher than current precision
digits = min((int)numS.size(), digits);
// replace unwanted precision with zeroes
numS.replace(digits, numS.size() - digits, numS.size() - digits, '0');
// if we had a decimal, add it back
if (decimalIndex != -1)
numS.insert(numS.begin() + decimalIndex, '.');
return atof(numS.c_str());
}
This will never work since a double is not a decimal type. Truncating what you think are a certain number of decimal digits will merely introduce a new set of joke digits at the end. It could even be pernicious: e.g. 0.125 is an exact double, but neither 0.12 nor 0.13 are.
If you want to work in decimals, then use a decimal type, or a large integral type with a convention that part of it holds a decimal portion.
I disagree with "So the best approach, is to only use up to the precision all 3 sources agree on."
If these are different measurements of a physical quantity, or represent rounding error due to different ways of calculating from measurements, you will get a better estimate of the true value by taking their mean than by forcing the digits they disagree about to any arbitrary value, including zero.
The ultimate justification for taking the mean is the Central Limit Theorem, which suggests treating your measurements as a sample from a normal distribution. If so, the sample mean is the best available estimate of the population mean. Your truncation process will tend to underestimate the actual value.
It is generally better to keep every scrap of information you have through the calculations, and then remember you have limited precision when outputting results.
As well as giving a better estimate, taking the mean of three numbers is an extremely simple calculation.
In fortran I have to round latitude and longitude to one digit after decimal point.
I am using gfortran compiler and the nint function but the following does not work:
print *, nint( 1.40 * 10. ) / 10. ! prints 1.39999998
print *, nint( 1.49 * 10. ) / 10. ! prints 1.50000000
Looking for both general and specific solutions here. For example:
How can we display numbers rounded to one decimal place?
How can we store such rounded numbers in fortran. It's not possible in a float variable, but are there other ways?
How can we write such numbers to NetCDF?
How can we write such numbers to a CSV or text file?
As others have said, the issue is the use of floating point representation in the NetCDF file. Using nco utilities, you can change the latitude/longitude to short integers with scale_factor and add_offset. Like this:
ncap2 -s 'latitude=pack(latitude, 0.1, 0); longitude=pack(longitude, 0.1, 0);' old.nc new.nc
There is no way to do what you are asking. The underlying problem is that the rounded values you desire are not necessarily able to be represented using floating point.
For example, if you had a value 10.58, this is represented exactly as 1.3225000 x 2^3 = 10.580000 in IEEE754 float32.
When you round this to value to one decimal point (however you choose to do so), the result would be 10.6, however 10.6 does not have an exact representation. The nearest representation is 1.3249999 x 2^3 = 10.599999 in float32. So no matter how you deal with the rounding, there is no way to store 10.6 exactly in a float32 value, and no way to write it as a floating point value into a netCDF file.
YES, IT CAN BE DONE! The "accepted" answer above is correct in its limited range, but is wrong about what you can actually accomplish in Fortran (or various other HGL's).
The only question is what price are you willing to pay, if the something like a Write with F(6.1) fails?
From one perspective, your problem is a particularly trivial variation on the subject of "Arbitrary Precision" computing. How do you imagine cryptography is handled when you need to store, manipulate, and perform "math" with, say, 1024 bit numbers, with exact precision?
A simple strategy in this case would be to separate each number into its constituent "LHSofD" (Left Hand Side of Decimal), and "RHSofD" values. For example, you might have an RLon(i,j) = 105.591, and would like to print 105.6 (or any manner of rounding) to your netCDF (or any normal) file. Split this into RLonLHS(i,j) = 105, and RLonRHS(i,j) = 591.
... at this point you have choices that increase generality, but at some expense. To save "money" the RHS might be retained as 0.591 (but loose generality if you need to do fancier things).
For simplicity, assume the "cheap and cheerful" second strategy.
The LHS is easy (Int()).
Now, for the RHS, multiply by 10 (if, you wish to round to 1 DEC), e.g. to arrive at RLonRHS(i,j) = 5.91, and then apply Fortran "round to nearest Int" NInt() intrinsic ... leaving you with RLonRHS(i,j) = 6.0.
... and Bob's your uncle:
Now you print the LHS and RHS to your netCDF using a suitable Write statement concatenating the "duals", and will created an EXACT representation as per the required objectives in the OP.
... of course later reading-in those values returns to the same issues as illustrated above, unless the read-in also is ArbPrec aware.
... we wrote our own ArbPrec lib, but there are several about, also in VBA and other HGL's ... but be warned a full ArbPrec bit of machinery is a non-trivial matter ... lucky you problem is so simple.
There are several aspects one can consider in relation to "rounding to one decimal place". These relate to: internal storage and manipulation; display and interchange.
Display and interchange
The simplest aspects cover how we report stored value, regardless of the internal representation used. As covered in depth in other answers and elsewhere we can use a numeric edit descriptor with a single fractional digit:
print '(F0.1,2X,F0.1)', 10.3, 10.17
end
How the output is rounded is a changeable mode:
print '(RU,F0.1,2X,RD,F0.1)', 10.17, 10.17
end
In this example we've chosen to round up and then down, but we could also round to zero or round to nearest (or let the compiler choose for us).
For any formatted output, whether to screen or file, such edit descriptors are available. A G edit descriptor, such as one may use to write CSV files, will also do this rounding.
For unformatted output this concept of rounding is not applicable as the internal representation is referenced. Equally for an interchange format such as NetCDF and HDF5 we do not have this rounding.
For NetCDF your attribute convention may specify something like FORTRAN_format which gives an appropriate format for ultimate display of the (default) real, non-rounded, variable .
Internal storage
Other answers and the question itself mention the impossibility of accurately representing (and working with) decimal digits. However, nothing in the Fortran language requires this to be impossible:
integer, parameter :: rk = SELECTED_REAL_KIND(radix=10)
real(rk) x
x = 0.1_rk
print *, x
end
is a Fortran program which has a radix-10 variable and literal constant. See also IEEE_SELECTED_REAL_KIND(radix=10).
Now, you are exceptionally likely to see that selected_real_kind(radix=10) gives you the value -5, but if you want something positive that can be used as a type parameter you just need to find someone offering you such a system.
If you aren't able to find such a thing then you will need to work accounting for errors. There are two parts to consider here.
The intrinsic real numerical types in Fortran are floating point ones. To use a fixed point numeric type, or a system like binary-coded decimal, you will need to resort to non-intrinsic types. Such a topic is beyond the scope of this answer, but pointers are made in that direction by DrOli.
These efforts will not be computationally/programmer-time cheap. You will also need to take care of managing these types in your output and interchange.
Depending on the requirements of your work, you may find simply scaling by (powers of) ten and working on integers suits. In such cases, you will also want to find the corresponding NetCDF attribute in your convention, such as scale_factor.
Relating to our internal representation concerns we have similar rounding issues to output. For example, if my input data has a longitude of 10.17... but I want to round it in my internal representation to (the nearest representable value to) a single decimal digit (say 10.2/10.1999998) and then work through with that, how do I manage that?
We've seen how nint(10.17*10)/10. gives us this, but we've also learned something about how numeric edit descriptors do this nicely for output, including controlling the rounding mode:
character(10) :: intermediate
real :: rounded
write(intermediate, '(RN,F0.1)') 10.17
read(intermediate, *) rounded
print *, rounded ! This may look not "exact"
end
We can track the accumulation of errors here if this is desired.
The `round_x = nint(x*10d0)/10d0' operator rounds x (for abs(x) < 2**31/10, for large numbers use dnint()) and assigns the rounded value to the round_x variable for further calculations.
As mentioned in the answers above, not all numbers with one significant digit after the decimal point have an exact representation, for example, 0.3 does not.
print *, 0.3d0
Output:
0.29999999999999999
To output a rounded value to a file, to the screen, or to convert it to a string with a single significant digit after the decimal point, use edit descriptor 'Fw.1' (w - width w characters, 0 - variable width). For example:
print '(5(1x, f0.1))', 1.30, 1.31, 1.35, 1.39, 345.46
Output:
1.3 1.3 1.4 1.4 345.5
#JohnE, using 'G10.2' is incorrect, it rounds the result to two significant digits, not to one digit after the decimal point. Eg:
print '(g10.2)', 345.46
Output:
0.35E+03
P.S.
For NetCDF, rounding should be handled by NetCDF viewer, however, you can output variables as NC_STRING type:
write(NetCDF_out_string, '(F0.1)') 1.49
Or, alternatively, get "beautiful" NC_FLOAT/NC_DOUBLE numbers:
beautiful_float_x = nint(x*10.)/10. + epsilon(1.)*nint(x*10.)/10./2.
beautiful_double_x = dnint(x*10d0)/10d0 + epsilon(1d0)*dnint(x*10d0)/10d0/2d0
P.P.S. #JohnE
The preferred solution is not to round intermediate results in memory or in files. Rounding is performed only when the final output of human-readable data is issued;
Use print with edit descriptor ‘Fw.1’, see above;
There are no simple and reliable ways to accurately store rounded numbers (numbers with a decimal fixed point):
2.1. Theoretically, some Fortran implementations can support decimal arithmetic, but I am not aware of implementations that in which ‘selected_real_kind(4, 4, 10)’ returns a value other than -5;
2.2. It is possible to store rounded numbers as strings;
2.3. You can use the Fortran binding of GIMP library. Functions with the mpq_ prefix are designed to work with rational numbers;
There are no simple and reliable ways to write rounded numbers in a netCDF file while preserving their properties for the reader of this file:
3.1. netCDF supports 'Packed Data Values‘, i.e. you can set an integer type with the attributes’ scale_factor‘,’ add_offset' and save arrays of integers. But, in the file ‘scale_factor’ will be stored as a floating number of single or double precision, i.e. the value will differ from 0.1. Accordingly, when reading, when calculating by the netCDF library unpacked_data_value = packed_data_value*scale_factor + add_offset, there will be a rounding error. (You can set scale_factor=0.1*(1.+epsilon(1.)) or scale_factor=0.1d0*(1d0+epsilon(1d0)) to exclude a large number of digits '9'.);
3.2. There are C_format and FORTRAN_format attributes. But it is quite difficult to predict which reader will use which attribute and whether they will use them at all;
3.3. You can store rounded numbers as strings or user-defined types;
Use write() with edit descriptor ‘Fw.1’, see above.
I'm programming a calculator on an Arduino and I'm trying to calculate pow and writing it to a string (result). This is my code:
dtostrf(exp(n*log(x)), 0, 5, result); // x ^ n
2 ^ 2 = 4.00000 // works fine
10 ^ 5 = 99999.9770 // should be 100000
What's wrong with my code and how can I always get the right result?
I mean how can I round it but still be able to use doubles ( e.g. 5.2 ^ 3.123 )
You're just hitting rounding errors. There's nothing you can do about this, except revert to an integer-based approach whenever the inputs are integers.
You could condition on whether the inputs are integers, and if so then use integer arithmetic; if not, then use doubles. But using exp and log will always introduce rounding errors, so you can't expect exact answers with that approach.
More precisely, to use integer arithmetic, you need the base to be an integer and the exponent to be a non-negative integer.
Since you are programming a calculator, speed is not your concern but the number of reliable digits is. So, you could try to use a double precision library. It uses 64-Bit-doubles but has only about 200 FLOPS at 16MHz CPU clock and much less at higher-order calculations like exp(), log(), or sin(). Thus, it will take a second after having typed in the digits and pressed the enter button but this was also the case with the old 8-Bit-based pocket caluclators.
See this Link (only in German)
How can I convert two unsigned integers that represent the digit and decimal part of a float, into one float.
I know there are a few ways todo this, like converting the decimal component into a float and multiplying it to get a decimal and added it to the digit, but that does not seem optimal.
I'm looking for the optimal way todo this.
/*
* Get Current Temp in Celecius.
*/
void GetTemp(){
int8_t digit = 0; // Digit Part of Temp
uint16_t decimal = 0; // Decimal Part of Temp
// define variable that will hold temperature digit and decimal part
therm_read_temperature(&temperature, &decimal); //Gets the current temp and sets the variables to the value
}
I want to take the Digit and Decimal parts and convert them to a float type, such that it looks like digit.decimal .
It might look like this in end, but I want to find the MOST optimal solution.
/*
* Get Current Temp in Celecius.
*/
float GetTemp(){
int8_t digit = 0; // Digit Part of Temp
uint16_t decimal = 0; // Decimal Part of Temp
// define variable that will hold temperature digit and decimal part
therm_read_temperature(&temperature, &decimal); //Gets the current temp and sets the variables to the value
float temp = SomeFunction(digit, decimal); //This could be a expression also.
return temp;
}
////UPDATE/// - July 5th
I was able to get the source code instead of leveraging just the library. I posted it in this GIST DS12B20.c.
temperature[0]=therm_read_byte();
temperature[1]=therm_read_byte();
therm_reset();
//Store temperature integer digits and decimal digits
digit=temperature[0]>>4;
digit|=(temperature[1]&0x7)<<4;
//Store decimal digits
decimal=temperature[0]&0xf;
decimal*=THERM_DECIMAL_STEPS_12BIT;
*digit_part = digit;
*decimal_part = decimal;
Although the function will not force us to return separate parts as digit and decimal, reading from the temperature sensor seems to require this (unless i'm missing something and it can be retrieved as a float).
I think the original question still stands as what is the optimal way to make this into a float in C (this is for use with AVR and an 8bit microprocessor, making optimization key) using the two parts or to be able to retrieve it directly as a float.
What you are really running into is using fixed-point numbers. These can be represented in two ways: either as a single integer with a known magnitude or multiplier (ie. "tenths", "hundredths", "thousandths", and so on; example: value from a digital scale in ten-thousandths of a gram, held in a 32-bit integer -- you divide by 10000 to get grams), or as two integers, with one holding the "accumulated" or "integer" value, and the other holding the "fractional" value.
Take a look at the <stdfix.h> header. This declares types and functions to hold these fixed-point numbers, and perform math with them. When adding fractional parts, for example, you have to worry about rolling into the next whole value, for which you then want to increment the accumulator of the result. By using the standard functions you can take advantage of built-in processor capabilities for fixed-point math, such as those present in the AVR, PIC and MPS430 microcontrollers. Perfect for temperature sensors, GPS receivers, scales (balances), and other sensors that have rational numbers but only integer registers or arithmetic.
Here is an article about it: "Fixed Point Extensions to the C Programming Language", https://sestevenson.wordpress.com/2009/09/10/fixed-point-extensions-to-the-c-programming-language/
To quote a portion of that article:
I don’t think the extensions simplify the use of fixed types very
much. The programmer still needs to know how many bits are allocated
to integer and fractional parts, and how the number and positions of
bits may change (during multiplication for example). What the
extensions do provide is a way to access the saturation and rounding
modes of the processor without writing assembly code. With this level
of access, it is possible to write much more efficient C code to
handle these operations.
Scott G. Hall
Raleigh, NC, USA
Your question contains a wrong assumption.
If you're given a decimal string and want a floating-point value, the first step should generally not be to turn it into two integers.
For instance, consider the numbers 2.1 and 2.01. What's the "decimal part" in each case? 1 and 01? Both of those equal 1. That's no good.
The only case in which this approach makes any sense is where you have a fixed number of places after the decimal point -- in which case maybe 2.1 turns into (2,1000) and 2.01 turns into (2,100), or something. But unless you've got a positive reason for doing that (which I strongly doubt) you should not do it this way.
In particular, unless therm_read_temperature is a function someone else is providing you with and whose interface you can't influence, you should make that function behave differently -- e.g., just returning a float. (If it is a function someone else is providing and whose interface you can't influence, then to get a useful answer here you'll need to tell us exactly what it's defined to do.)