var s:string;
begin
writestr(s, 2.5625:4);
writeln(s);
end.
Given a String '2.5625' I want to return the number 2.5625 NOT 2.562500023841 . . . . . .
Thanks for the input Thaddy but I have no problem in adjusting the DISPLAY of a float (or any number really) it's making the VALUE accurate to the real number of decimal places which is my difficulty.Code: [Select]var s:string;
begin
writestr(s, 2.5625:4);
writeln(s);
end.
@J-G - not sure if the following helps your understanding, but I'll tryAnd you have succeded! - I was aware of the issues concerned with accurate representation of 'floats' but your explanation is succinct.
* you are fiddling with two values 2.55 & 2.5625 which are very different and maybe even got mixed up in your testing such that the results confused youI wasn't 'confused', I confused you by referencing two different subjects and providing a very poor (inaccurate) example :-[ - I was just trying to indicate that the were values beyond the d.places needed.
- 2.5625 = 41 / 16, that means this value can exactly be represented as a float (single,double or extended) because the divisor is a small power of 2 <= as Bart already pointed outSince the minimum allowable figure is 20, I hadn't seen 41/16 - the first 'good' pairing was at 82/32.
- 2.55 = 51/20, that means this value can not be exactly represented as a float (single, double or extended) because the divisor is a multiple of 5I completely understand that logic and even see why using 'Double' is better - but still cannot see how A=B does return true ??? (They are declared : single) - I'm grateful that it does!
- the float will contain the closest approximation to '2.55' which, in case of single, is '2.54999952'
* you can calculate ad nauseam wrt 2.55 you'll simply never get to 'good = TRUE'
* when calculating wrt 2.5625 you'll get 'good = TRUE' because the floating point value is exact
However, I am currently at a loss to explain where / how you got the info that '2.5625 = 2.562500023841...' - there has to be an intermediate step you overlooked that changed the value to an inexact approximation.Again - appologies for that particular confusion :-[
Cheers,
MathMan
I've now declared 'Ratio' as Double and can confirm that the 2.55 does return correctly (no trailing rubbish), however, 'Good' still returns false with the (T1*Ratio) = Int(T1*Ratio) test and True with the A=B test - - - that is the logic that defeats me.
Whilst I understand the problem of exact representaion of decimal numbers, that doesn't tell me why assigning A (a Single) to (T1*Ratio) and B (another Single) to Int(A) -- effectively Int(T1*Ratio) --- returns in a different Boolean result than a direct comparison of the two.I've now declared 'Ratio' as Double and can confirm that the 2.55 does return correctly (no trailing rubbish), however, 'Good' still returns false with the (T1*Ratio) = Int(T1*Ratio) test and True with the A=B test - - - that is the logic that defeats me.Single and double types (IEEE 754) can't always represent exact decimal number and that is the reason your test fails.
What you can do is compare difference between two number and if it is within certain precision you can say there are same, maybe something like:That may well be a viable option as well - I have tried the 'SameValue((T1*Ratio),Int(T1*Ratio),Tol) -- where I've set 'Tol' to various values such as 0.001 and 0.000001 -- but without success.
Good := abs(T1*Ratio - Int(T1*Ratio)) < 0.00001;
Maybe you could use 'Currency' type. You won't have problems like this with Currency, but it is limited to 4 decimal places.Regrettably there is a likelyhood that a viable Ratio can have as many as 8 d.Places - certainly 6 - but thanks for the suggestion.#
Whilst I understand the problem of exact representaion of decimal numbers, that doesn't tell me why assigning A (a Single) to (T1*Ratio) and B (another Single) to Int(A) -- effectively Int(T1*Ratio) --- returns in a different Boolean result than a direct comparison of the two.This may happen. It happens if you do in one case
Please provide "exactly" the two code you are speaking about that provided different resultI did so in the very first post.
Good := (T1*Ratio) = Int(T1*Ratio);with T1:byte = 20 and Ratio : single =2.54999952
Good := (G1*Ratio) = Int(G1*Ratio);with again G1:byte = 20 and Ratio : single =2.54999952
A := T1*Ratio;with A and B : single that makes me suspect that it is the problem I pointed out
B := Int(A);
Good := A = B;
I guess that this a test to determine whether the product of two numbers is an integer. The problem with the '=' operator has already be mentioned by others: always use SameValue() or equivalent, i.e.
Good := (G1*Ratio) = Int(G1*Ratio);
... you are comparing two "single value" with Int value after trouncation at singleI think that you are right on the money Paolo - to some extent I was only pursuing the issue out of curiosity, but hopefully I've learned a valuable lesson which may also be useful to others :)
and as I said before the results can be different because single values are differenet wrt respective extended values.
very likely if you use Extended everywhere all is "good(true)".
Just playing "Oh well"I do appreciate your input Thaddy (though not your choice of music ;D - I'm currently rehearsing Haydn's 'The Creation') and I will evaluate ReadStr & WriteStr further - just not today. :o
https://www.youtube.com/watch?v=0yq-Fw7C26Y
Why are you trying to mimic morons????
https://www.youtube.com/watch?v=vZw35VUBdzo
Just playing "Oh well"
https://www.youtube.com/watch?v=0yq-Fw7C26Y
Thanks Jamie - your post nearly got lost in the crowd - but I do like to acknowledge all contributions to any threads that I initiate.
procedure TForm1.Button1Click(Sender: TObject); Var D:Single = 2.54999952; begin D := RoundTo(D,-4)*20; Caption := Int(D).Tostring; end;
Produces 51
Does that help you at all?
I've used the Currency type many times for Fixed point. the RTL seems to know how to handle it.It may have HUGE rounding issues if applied thoughtlessly...
[...]
The Fix point type does not have the rounding issues etc.
This code calculates the log of 0.5 and then the exp function of the result. Since ln and exp are inverse functions the final result should be equal to the initial value (0.5) - it turns out the be 1.0. however.
var c1, c2, c3: Currency; begin c1 := 0.5; c2 := ln(c1); c3 := exp(c3); WriteLn(c1); WriteLn(c2); WriteLn(c3); end.
Oh...
But then I don't understand this data type at all. I thought it is stored as an Int64 (https://www.freepascal.org/docs-html/ref/refsu5.html: "The currency type is a fixed-point real data type which is internally used as an 64-bit integer type (automatically scaled with a factor 10000)"). How can it yield the exact result when all decimals after the 4th are truncated in the first calculation? Well, maybe, when the exp is calculated based on the rounded value the error is after the 4th decimal again and does not appear after truncating the final result. But such a coincidence is hard to believe. I am sure that there must be better examples...
Calculation with double:
d1 = -9.800000000
d2 = 0.000055452
d3 = -9.800000000 (should be equal to d1)
Calculation with currency:
c1 = -9.8000
c2 = 0.0001
c3 = -9.2103 (should be equal to c1)
I am sure that there must be better examples...
Here is a variant of the exp/ln sample with shows a difference although not as large as I had written. The difference becomes evident when an intermediate currency result gets close to 0.0000. In this particular example there will even be a runtime error when c1 is -9.9 or smaller so that the intermediate result is 0.0000 in the currency case because the ln(0.0000) cannot be calculated.
If you need more than 4 decimal places then use single or double, not currency. You could still use single or double for intermediate calculation and use currency for final value if that fits your need.Why should I switch number type for intermediate calculations? I never use Currency because I want the full accuracy of the calculation. I don't care about the appended '0's or '9's - even if this looks ugly on a quick and dirty output, its the highest accuracy than can be achieved on a computer with the finite resolution of single, double or extended. When it comes to display the results the numbers must be converted to strings anyway, and for this there are a lot of float-to-string conversion functions which can round to any number of decimals. Currency poses the risk of enhanced error propagation when multiple operations are chained. Even the requirement to have multiplication and division in a given order is against all that I am used to.
Currency is good only for adding monetary values, but even this can be done with single/double/extended since the error in them is way beyond the 4 decimals of Currency and never shows up in a string converted to 2 decimals.
t is that they basically do nothing else than SameValue(a, b, 1E-4).
If you need more than 4 decimal places then use single or double, not currency. You could still use single or double for intermediate calculation and use currency for final value if that fits your need.Why should I switch number type for intermediate calculations? I never use Currency because I want the full accuracy of the calculation. I don't care about the appended '0's or '9's - even if this looks ugly on a quick and dirty output, its the highest accuracy than can be achieved on a computer with the finite resolution of single, double or extended. When it comes to display the results the numbers must be converted to strings anyway, and for this there are a lot of float-to-string conversion functions which can round to any number of decimals. Currency poses the risk of enhanced error propagation when multiple operations are chained. Even the requirement to have multiplication and division in a given order is against all that I am used to.
Currency is good only for adding monetary values, but even this can be done with single/double/extended since the error in them is way beyond the 4 decimals of Currency and never shows up in a string converted to 2 decimals.
Did you read my post? I wrote: "never use the =, <, <= etc operators, use SameValue of the math unit (or combinations with it) instead."Currency is good only for adding monetary values, but even this can be done with single/double/extended since the error in them is way beyond the 4 decimals of Currency and never shows up in a string converted to 2 decimals.
t is that they basically do nothing else than SameValue(a, b, 1E-4).
program Project1; var i : integer; d1, d2 : double; sd1, sd2 : double; c1, c2 : currency; sc1, sc2 : currency; begin d1 := 0.1; d2 := 0.2; sd1 :=0; sd2 := 0; for I := 1 to 10 do sd1 := sd1 + d1; for I := 1 to 5 do sd2 := sd2 +d2; WriteLn('sd1 = sd2 : ', sd1 = sd2); c1 := 0.1; c2 := 0.2; sc1 :=0; sc2 := 0; for I := 1 to 10 do sc1 := sc1 + c1; for I := 1 to 5 do sc2 := sc2 +c2; WriteLn('sc1 = sc2 : ', sc1 = sc2); readln; end.
Good example. But I stay with my opinion that currency is not good for anything else than addition and subtraction and multiplication. Division already may go wrong if the programmer is not careful.
var cX: Currency = 0.0; dX: Double = 0.0; i: Integer; s: String; begin For i := 1 to 10000 do begin cX := cX + 10000000.07; dX := dX + 10000000.07; end; WriteLn(FormatFloat('0.0000', cX)); WriteLn(FormatFloat('0.0000', dX)); end.
Of course this kind of calculation is a bit unfair ...Agreed ;-)
It's interesting to see that each 'type' has issues with certain inputs, (the obvious ones being numbers ending in a 5 - [1.25] say) though D & E seem to have the same issue - try 5.125 . . . .
The ReadStr/WriteStr also threw up some unexpected issues. ReadStr accepted the use of a TLable.Caption but WriteStr does not, it demands an intermediary 'String' variable which is currently beyond my understanding.
... So 5.125 can only be approximated and the math system can't simply show 5.125, because what if you did try to use the number that 5.125 is approximated as?Well, as a special case 5.125 is exactly stored in double as 0.640625 × (2 ^ 3)
My point was that using my test program, all three 'types' return an incorrect value -... So 5.125 can only be approximated and the math system can't simply show 5.125, because what if you did try to use the number that 5.125 is approximated as?Well, as a special case 5.125 is exactly stored in double as 0.640625 × (2 ^ 3)
My point was that using my test program, all three 'types' return an incorrect value -... So 5.125 can only be approximated and the math system can't simply show 5.125, because what if you did try to use the number that 5.125 is approximated as?Well, as a special case 5.125 is exactly stored in double as 0.640625 × (2 ^ 3)
Currency = 5.1248,
Double &
Extended = 5.12499999999999
Don't underestimate fixed floating point. Depending on your use case they might be better (https://web.archive.org/web/20110710010949/https://facepunch.com/content/119-Why-floats-suck.). ;)Did you mean fixed _decimal_ point ?
That doesn't really make a difference. It's about resolution. Even if you have an ultimately exact analog computer, you have to feed it with starting values, and at the end you have to extract the answer. Analog-to-Decimal-converters don't have infinite precision. That's the whole point.
If you don't want any rounding errors:
Use an analogue computer, not a binary one.
That doesn't really make a difference. It's about resolution. Even if you have an ultimately exact analog computer, you have to feed it with starting values, and at the end you have to extract the answer. Analog-to-Decimal-converters don't have infinite precision. That's the whole point.
If you don't want any rounding errors:
Use an analogue computer, not a binary one.