Frac function losing precision - delphi

I have a TDateTime variable which is assigned a value at runtime of 40510.416667. When I extract the time to a TTime type variable using the Frac function, it sets it to 0.41666666666. Why has it changed the precision of the value and is there a workround to retain the precision from the original value ie. to set it to 0.416667.

TDateTime is a floating point number. Some numbers can't be represented exactly as a floating point number. 0.416667 / 0.41666666666 would seem to be another one.
You can round to 5 or 6 digits for display. That gets you accuracy to around 1 second.

What Every Computer Scientist Should Know About Floating-Point Numbers should help, as should SO's own Precision of Floating Point - that will give you some detailed information to go with Jeff's answer.

One of the reason for the loss of precision is that TDateTime is a double, and Frac's parameter and return value is of type Extended.
When converting floating points from one type to another, some precision can be lost. (Same goes when doing arithmetic on them).
To compare float value correctly, you should use the CompareValue function from the unit Math.

Thanks for all your help on this, much appreciated. To get round my problen that was arising due to the change in precision I used the CompareTime function instead of the >= or <= operators for comparing the times.

Related

Delphi Roundto and FormatFloat Inconsistency

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.

Lua significant figures

I'm trying to make a function that rounds a number up to a certain number of significant figures given by the user, for example if the user gives me the number
234.235534 with 5 significant numbers, the function should return 234.24
I think you're looking for the [fs]?printf's %g modifier.
converts floating-point number to decimal or decimal exponent notation
depending on the value and the precision.
where, the precision is defined by:
. followed by integer number or *, or neither that specifies
precision of the conversion. In the case when * is used, the
precision is specified by an additional argument of type int. If the
value of this argument is negative, it is ignored. If neither a number
nor * is used, the precision is taken as zero.
So, you want:
> return ("%.5g"):format(234.235534)
234.24
> return ("%.6g"):format(x)
234.236
I'm not much of a programmer, but I came up with this for my own use after I was disappointed by other rounding functions people recommended in lua. This should do what you asked.
function sigFig(num,figures)
local x=figures - math.ceil(math.log10(math.abs(num)))
return(math.floor(num*10^x+0.5)/10^x)
end
now in terms of significant digits, it won't add additional zeros to a number to signify precision. For example:
sigFig(234.235534,5) will yield 234.24
sigFig(234.0000001,6) will yield 234.0, not 234.000

Delphi - Comparing float values

I have a function that returns a float value like this:
1.31584870815277
I need a function that returns TRUE comparing the value and the two numbers after the dot.
Example:
if 1.31584870815277 = 1.31 then ShowMessage('same');
Sorry for my english.
Can someone help me? Thanks
Your problem specification is a little vague. For instance, you state that you want to compare the values after the decimal point. In which case that would imply that you wish 1.31 to be considered equal to 2.31.
On top of this, you will need to specify how many decimal places to consider. A number like 1.31 is not representable exactly in binary floating point. Depending on the type you use, the closest representable value could be less than or greater than 1.31.
My guess is that what you wish to do is to use round to nearest, to a specific number of decimal places. You can use the SameValue function from the Math unit for this purpose. In your case you would write:
SameValue(x, y, 0.01)
to test for equality up to a tolerance of 0.01.
This may not be precisely what you are looking for, but then it's clear from your question that you don't yet know exactly what you are looking for. If your needs are specifically related to decimal representation of the values then consider using a decimal type rather than a binary type. In Delphi that would be Currency.
If speed isn't the highest priority, you can use string conversion:
if Copy(1.31584870815277.ToString, 1, 4) = '1.31' then ShowMessage('same');

What is the point of using 'f' when assigning a value to a CGFloat?

I see this all the time:
CGFloat someCGFloat = 1.2f;
Why is the 'f' used? If the CGFloat is defined as float, the value will be converted to a float, and if the CGFloat is defined as a double, the value will be converted to a double.
Is it just to make sure a conversion from double to float doesn't occur? What's the point of doing that? Also, wouldn't the compiler take care of this?
EDIT: Hmmm…which answer to accept…both are very good!
1.0 by default is double, if the right value is 1.2 there is an implicit cast, and the value gets casted from double to float (the cast isn't a runtime operation). In this case it's not important to call it 1.2f. Programmers mostly abuse it, but there are cases where it's really important.
For example:
float var= 1.0e-45;
NSLog(#"%d",var==1.0e-45);
This prints zero, because 1.0e-45 is too small to be stored into a single precision floating point variable, so it becomes equal to zero. Writing var==1.0e-45f changes the result.
Using format specifiers is important mostly when writing expressions, and since the left value is a float you expect that also the expression gets treated as a float, but that's not what happens.
A more striking case is when using the l format specifier on a number that gets shifted so much to become zero, and get surprised about the result:
long var= 1<<32; // I assume that an int takes 4 bytes and a long 8 bytes
The result is zero, and writing 1l<<32 completely changes the result.
In your snippet, just an assignment, you don't need the 'f' suffix, and in fact you shouldn't use it. If CGFloat is single precision (like in iOS) then your value will be stored single precision with or without the 'f' and if CGFloat is double precision (like on Mac OS) then you'll be unnecessarily creating a single precision value to be stored double precision.
On the other hand, if you're doing arithmetic you should be careful to use 'f' or not use it as appropriate. If you're working with single precision and include a literal like '1.2' without the 'f' then the compiler will promote the other operands to double precision. And if you're working with double precision and you include the 'f' then (like the assignment on Mac OS) you'll be creating a single precision value only to have it immediately converted to double.

Why does calculation using real give different result from one using int?

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.

Resources