I'm getting a rounding oddity in Delphi 2010, where some numbers are rounding down in roundto, but up in formatfloat.
I'm entirely aware of binary representation of decimal numbers sometimes giving misleading results, but in that case I would expect formatfloat and roundto to give the same result.
I've also seen advice that this is the sort of thing "Currency" should be used for, but as you can see below, Currency and Double give the same results.
program testrounding;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,Math;
var d:Double;
c:Currency;
begin
d:=534.50;
c:=534.50;
writeln('Format: ' +formatfloat('0',d));
writeln('Roundto: '+formatfloat('0',roundto(d,0)));
writeln('C Format: ' +formatfloat('0',c));
writeln('C Roundto: '+formatfloat('0',roundto(c,0)));
readln;
end.
The results are as follows:
Format: 535
Roundto: 534
C Format: 535
C Roundto: 534
I've looked at Why is the result of RoundTo(87.285, -2) => 87.28 and the suggested remedies do not seem to apply.
First of all, we can remove Currency from the question, because the two functions that you use don't have Currency overloads. The value is converted to an IEEE754 floating point value and then follows the same path as your Double code.
Let's look at RoundTo first of all. It is quick to check, using the debugger, or an additional Writeln that RoundTo(d,0) = 534. Why is that?
Well, the documentation for RoundTo says:
Rounds a floating-point value to a specified digit or power of ten using "Banker's rounding".
Indeed in the implementation of RoundTo we see that the rounding mode is temporarily switched to TRoundingMode.rmNearest before being restored to its original value. The rounding mode only applies when the value is exactly half way between two integers. Which is precisely the case we have here.
So Banker's rounding applies. Which means that when the value is exactly half way between two integers, the rounding algorithm chooses the adjacent even integer.
So it makes sense that RoundTo(534.5,0) = 534, and equally you can check that RoundTo(535.5,0) = 536.
Understanding FormatFloat is quite a different matter. Quite frankly its behaviour is somewhat opaque. It performs an ad hoc rounding in code that differs for different platforms. For instance it is assembler on 32 bit Windows, but Pascal on 64 bit Windows. The overall approach appears to be to take the mantissa of the floating point value, convert it to an integer, convert that to text digits, and then perform the rounding based on those text digits. No respect is paid to the current rounding mode when the rounding is performed, and the algorithm appears to implement the round half away from zero policy. However, even that is not implemented robustly for all possible floating point values. It works correctly for your value, but for values with more digits in the mantissa the algorithm breaks down.
In fact it is fairly well known that the Delphi RTL routines for converting between floating point values and text are fundamentally broken by design. There are no routines in the Delphi RTL that can correctly convert from text to float, or from float to text. In fact, I have recently implemented my own conversion routines, that do this correctly, based on existing open source code used by other language runtimes. One of these days I will get around to publishing this code for use by others.
I'm not sure what your exact needs are, but if you are wishing to exert some control over rounding, then you can do so if you take charge of the rounding. Whilst RoundTo always uses Banker's rounding, you can instead use Round which uses the current rounding mode. This will allow you to perform the round using the rounding algorithm of your choice (by calling SetRoundMode), and then you can convert the rounded value to text. That's the key. Keep the value in an arithmetic type, perform the rounding, and only convert to text at the very last moment, after the correct rounding has been applied.
In this case, the value 534.5 is exactly representable in Double precision.
Looking into source code, reveals that the FormatFloat function rounds upwards if the last pending digit is 5 or more.
RoundTo uses the Banker's rounding, and rounds to nearest even number (534) in this case.
Related
I have problem with comparison of two variables of "Real" type. One is a result of mathematical operation, stored in a dataset, second one is a value of an edit field in a form, converted by StrToFloat and stored to "Real" variable. The problem is this:
As you can see, the program is trying to tell me, that 121,97 is not equal to 121,97... I have read
this topic, and I am not copletely sure, that it is the same problem. If it was, wouldn't be both the numbers stored in the variables as an exactly same closest representable number, which for 121.97 is 121.96999 99999 99998 86313 16227 83839 70260 62011 71875 ?
Now let's say that they are not stored as the same closest representable number. How do I find how exactly are they stored? When I look in the "CPU" debugging window, I am completely lost. I see the adresses, where those values should be, but nothing even similar to some binary, hexadecimal or whatever representation of the actual number... I admit, that advanced debugging is unknown universe to me...
Edit:
those two values really are slightly different.
OK, I don't need to understand everything. Although I am not dealing with money, there will be maximum 3 decimal places, so "currency" is the way out
BTW: The calculation is:
DATA[i].Meta.UnUsedAmount := DATA[i].AMOUNT - ObjQuery.FieldByName('USED').AsFloat;
In this case it is 3695 - 3573.03
For reasons unknown, you cannot view a float value (single/double or real48) as hexadecimal in the watch list.
However, you can still view the hexadecimal representation by viewing it as a memory dump.
Here's how:
Add the variable to the watch list.
Right click on the watch -> Edit Watch...
View it as memory dump
Now you can compare the two values in the debugger.
Never use floats for monetary amounts
You do know of course that you should not use floats to count money.
You'll get into all sorts of trouble with rounding and comparisons will not work the way you want them too.
If you want to work with money use the currency type instead. It does not have these problems, supports 4 decimal places and can be compared using the = operator with no rounding issues.
In your database you use the money or currency datatype.
Studying for a test right now and can't seem to wrap my head around when to use "V" for a decimal instead of an actual decimal in PIC clauses. I've done some research but can't find anything I understand. Only been learning cobol for about a week, so is there like a rule of thumb here? Thanks for your time.
You use an actual decimal-point when you want to "output" a value which has decimal places, like a report line, a position on a screen, an item in an output file which is going to a "different" system which doesn't understand the format with an implied decimal pace.
That's what the V is, it is an implied decimal place. It tells the compiler where to align results from calculations, MOVEs, whatever. Computer chips, and the machine instructions they support, don't know about actual decimal points for their internal processing.
COBOL is a language with fixed-length fields. The machine instructions don't need to know where the decimal point is (effectively it can deal with everything as integer values) but the compiler does, and the compiler has to do the correct scaling and alignment of results.
Storing on your own files, use V, the implied decimal place.
For data which is to be "human readable" or read by a system which cannot understand your character set, cannot scale what looks like an integer, use an actual decimal-point, . (for computer-readable stuff, you can sometimes use a separate scaling factor, if that is more convenient for the receiving system).
Basically, V for internal, . for external, should be a rule of thumb to get you there.
Which COBOL are you using? I'm surprised it is not covered in your documentation.
I need to parse floating-point literals in C code using OCaml.
OCaml's float type is 64 bit. I have the string of the literal, its numeric value rounded to 64 bits and its kind (float, double or long double).
The problem are literals with a numeric value bigger than 64 bit:
long double literals
float literals with 'f'-suffix for which double rounding errors would occur if they wouldn't have the suffix.
OCaml's arbitrary-precision module can parse rational numbers from strings like "123/123", but not "123.123", "123e123", "0x1.23p-1" like they might appear in C.
Background: I do value analysis of C programs using CIL.
Double literals of any size and float literals with a numeric value that fits into 64 bit are always correctly represented. By rounding from double- to single-precision I can also reproduce double rounding errors.
I wrote my answer in the form of a blog post
To summarize some of the points here: you could interface strtold() and strtof() from OCaml. For the former, you would have to consider how you are going to store the result it produces, since there only is a point if long double is larger than double on your host architecture. There remains the problem that these functions are buggy in one of the most widely used C library. Very slightly buggy, but buggy for exactly the examples that are going to be of interest if you are doing this to study double rounding.
Another way is to write your own function, starting from another post in the blog you refer to.
Finally, the phrase "Even getting single-precision floats right requires me to parse literals with values bigger than 64 bit" that you use in the comments is still a strange way to put it. The intermediate format(s) in which you can parse the representation of a single-precision float before you round it to single-precision have to be lossless, otherwise there will be double rounding. Double rounding may be more or less difficult to exhibit depending on the precision of the lossy intermediate format, but using 80 bits or 128 bits binary floating-point formats is not going to remove the problem, just make it more subtle. In the simple algorithm that I recommend, the intermediate format is a fraction of two multiprecision integers.
I don't see the question in this question :)
Assuming that you need an ocaml parser for "C float literals" - the answer is - write one yourself, it is not very hard and you will have strict control on the implementation details and what "C float literal" actually means.
I have this code for example:
(a) writeln ('real => ', exp(3*Ln(3)):0:0); // return 27
(b) writeln ('int => ', int(exp(3*Ln(3))):0:0); // return 26
Is a bug?
The function calc 3^3 (exponent using ln and exp function), but conversion from real to int fail; in case (a) return 27, in case (b) return (26), when should be 27 both.
As i can solve it?
Thanks very much for help.
Ps: Too assign result to integer variable, using trunc, result not change.
No, it is not a bug. Computers simply don't have infinite precision, so the result is not exactly 27, but perhaps 26.999999999 or something. And so, when you int or trunc it, it ends up as 26. Use Round instead.
The expression you're printing evaluates to something slightly less than 27 due to the usual floating-point errors. The computer cannot exactly represent the natural logarithm of 3, so any further calculations based on it will have errors, too.
In comments, you claim exp(3*ln(3)) = 27.000, but you've shown no programmatic evidence for that assertion. Your code says exp(3*ln(3)) = 27, which is less precise. It prints that because you explicitly told WriteLn to use less precision. The :0:0 part isn't just decoration. It means that you want to print the result with zero decimal places. When you tell WriteLn to do that, it rounds to that many decimal places. In this case, it rounds up. But when you introduce the call to Int, you truncate the almost-27 value to exactly 26, and then WriteLn trivially rounds that to 26 before printing it.
If you tell WriteLn to display more decimal places, you should see different results. Consult the documentation for Write for details on what the numbers after the colons mean.
Working with floating points doesn't always give a 100% exact result. The reason being is that binary floating points variable can't always represent values exactly. The same thing is true about decimal numbers. If you take 1/3, in a 6 digit precision decimal, would be 0.333333. Then if you take 0.333333 * 3 = 0.999999. Int(0.999999) = 0
Here is some litterature about it...
What Every Computer Scientist Should Know About Floating-Point Arithmetic
You should also take a look at Rudy Velthuis' article:
http://rvelthuis.de/articles/articles-floats.html
Not a bug. It is just yet another example of how floating arithmetic works on a computer. Floating point arithmetic is but an approximation of how the real numbers work in mathematics. There is no guarantee, and there can be no such guarantee, that floating point results will be infinitely accurate. In fact, you should expect them to almost always be imprecise to some degree.
Given a double value like 1.00500000274996E-8, how do I convert it to it's non scientific format with a maximum number of digits after the decimal point - in this case with 8 digits it would be 1.00500000?
The conversion should not pad with zeros, so 2007 would come out as 2007, and 2012.33 and 2012.33.
I've tried lots of combinations using Format, FormatFloat, FloatToStrF but can't quite seem to hit the jackpot. Many thanks for any help.
Edit: I should clarify that I am trying to convert it to a string representation, without the Exponent (E) part.
FormatFloat('0.######################', 1.00500000274996E-8) should do the trick.
Output is: 0,0000000100500000274996
It will not output more digits than absolutely necessary.
See John Herbster's Exact Float to String Routines in CodeCentral. Perhaps not exactly what youre after but might be good starting point... CC item's description:
This module includes
(a) functions for converting a floating binary point number to its *exact* decimal representation in an AnsiString;
(b) functions for parsing the floating point types into sign, exponent, and mantissa; and
(c) function for analyzing a extended float number into its type (zero, normal, infinity, etc.)
Its intended use is for trouble shooting problems with floating point numbers.
His DecimalRounding routines might be of intrest too.