E.g. SimpleRoundTo(79.625, -2) gives the result 79.62 rather than the
expected 79.63.
We've got round it by a convoluted process of conversion from floats to
strings and back to floats again, but is there an alternative function
which works?
Trevor
SimpleRoundTo(1234567, 3) 1234000
SimpleRoundTo(1.234, -2) 1.23
SimpleRoundTo(1.235, -2) 1.24
SimpleRoundTo(-1.235, -2) -1.23
it is a bug
PS. I am not sure about last row thought ;)
I would think that Wiki shows it right -
http://en.wikipedia.org/wiki/Rounding - one just have to decide which method
to use
i.e. 79.625 would round to 79.62 but 79.635 would round to 79.64
Regards,
AndrewFG
John Herbster pointed out the same problem, and he suggests to replace
it with his DecimalRounding (JH1). The module is available from
CodeCentral, http://cc.codegear.com/Item.aspx?id=21909.
--
Willem van Deursen, The Netherlands
wvandeursen_nospam@nospam_carthago.nl
replace _nospam@nospam_ for @ to get a valid email address
www.carthago.nl
> i.e. 79.625 would round to 79.62 but 79.635 would round to 79.64
That is correct -- that is the IEEE spec, as I understand it.
--
Nick Hodges
Delphi Product Manager - CodeGear
http://blogs.codegear.com/nickhodges
Yes, when using Bankers rounding like RoundTo does.
But Trevor is using SimpleRoundTo and SimpleRoundTo is meant to do Arithmetic
rounding. So 79.62 is wrong.
--
Ralf Jansen
deepinvent Software GmbH - Viersen, Germany - http://www.deepinvent.com
Archiving E-mails with MailStore: http://www.mailstore.com
If you need correct rounding you should use bcd types (like currency in delphi)
and not real floatingpoint values. If you can't change that try Willem's suggestion.
Some decimal values can not be represented with absolut precision as binary
values. 79.625 shouldn't be a problem (it can be represented as binary something
like 1001111.101) but either your 79.625 value was the result of another
floatingpoint operation that evaluates to something slightly lower than 79.625
(you wouldn't see the ~real~ value in delphi's watch window while debugging if
you didn't mark the results as floatingpoints there to show the results as
floatingpoints) or one of the intermediate results inside SimpleRoundTo can't be
represented correctly as binary and therefore evaluates to something slightly
lower than 79.625
> But Trevor is using SimpleRoundTo and SimpleRoundTo is meant to do
> Arithmetic rounding. So 79.62 is wrong.
Ahh. Indeed --
> No Borland person was interested in fixing it up to now.
Rod --
It's in our internal system -- and has been "bandied around" a bit.
I've raised its visibility and it will get looked at afresh.
These errors exists since years.
http://qc.codegear.com/wc/qcmain.aspx?d=8070
No, it is fully documented "Bankers" rounding. Rounding is to the nearest
*even* value.
--
Wayne Niddery - Winwright, Inc (www.winwright.ca)
"The purpose of morality is to teach you, not to suffer and die, but to
enjoy yourself and live." - Ayn Rand
Is there or is there not a compiler switch or some other option
to make calculations performed with Delphi Currency type values
use the common method of rounding, also known as symmetric
arithmetic rounding or round-half-up (symmetric implementation)
rounding instead of bankers' rounding, also known as Round-to-even
rounding, unbiased rounding, convergent rounding, or statistician's
rounding?
A good programmer should know that there is no such
thing as "simple rounding" -- by that, I mean a
rounding rule that most programmers can agree on.
At least "bankers rounding" is a rule that most
programmers can agree on.
That aside, when ordinary programmers get caught in
the trap of using floating *binary* point numbers,
to represent (fixed or floating) decimal fraction
numbers and have to do any kind of rounding, then
they stumble all over themselves.
The problem, of course, is that general decimal
fractions cannot be exactly represented as
floating binary point numbers. This leads to
problems deciding what the floating binary point
number is really supposed to represent in decimal
fraction terms. My DecimalRounding package that
Willem mentioned solves this problem.
However, as someone ready to step into retirement
who has for many years tried both to educate the
ignorant and to push for incorporating actual
fixed and floating decimal fraction into Delphi,
I am really disappointed that I am still reading
such ill informed discussion of this problem.
Rgds, JohnH
--
Support the movement to add floating and fixed
decimal fraction numbers to Delphi.
http://qc.borland.com/wc/qcmain.aspx?d=28022
DecimalRounding (JH1) -- allows you to do about
nine kinds of decimal rounding with floating
binary point numbers.
http://cc.codegear.com/Item.aspx?id=21909
IEEE Number Analyzer (project source code) -- a
program that will convert a decimal fraction
number into the nearest possible floating binary
point number (i.e. single, double, or extended)
http://cc.codegear.com/item.aspx?id=23631
ExactFloatToStr -- a function for converting a
floating binary point number to its exact
decimal fraction values in a ASCII string.
http://cc.codegear.com/Item.aspx?id=19421
AFAIK Delphi has no build in rounding functions for the currency type.
For floatingpoint types SimpleRoundTo() does arithmetic rounding.
By default the RoundTo() method does bankers rounding but that can be changed
via the SetRoundMode() function.
| However, as someone ready to step into retirement
| who has for many years tried both to educate the
| ignorant and to push for incorporating actual
| fixed and floating decimal fraction into Delphi,
| I am really disappointed that I am still reading
| such ill informed discussion of this problem.
I retired seven years ago. I've been seeing these same discussions
since 1955. That's fifty-two years now. I'd be willing to bet my urn
that it will still be being bandied about in another fifty years. <g>
--
Q
08/03/2007 20:12:32
XanaNews Version 1.17.5.7 [Q's salutation mod]
Ralf,
At least for D7, SimpleRoundTo was screwed up. See
open QualityCentral reports #8070 and #8143:
_RoundTo and SimpleRoundTo are very sick_
http://qc.codegear.com/wc/qcmain.aspx?d=8070
_Replace RoundTo and SimpleRoundTo_
http://qc.codegear.com/wc/qcmain.aspx?d=8143
I would not recommend RoundTo or SimpleRoundTo to
anyone.
What is "arithmetic rounding"?
The types that I am familiar with include:
(Abbr: 'HalfEven'; Dscr: 'to nearest or even (bankers)'),
(Abbr: 'HalfPos' ; Dscr: 'to nearest or toward positive'),
(Abbr: 'HalfNeg' ; Dscr: 'to nearest or toward negative'),
(Abbr: 'HalfDown'; Dscr: 'to nearest or toward zero'),
(Abbr: 'HalfUp' ; Dscr: 'to nearest or away from zero'),
(Abbr: 'RndNeg' ; Dscr: 'toward negative (floor)'),
(Abbr: 'RndPos' ; Dscr: 'toward positive (ceil )'),
(Abbr: 'RndDown' ; Dscr: 'toward zero (trunc)'),
(Abbr: 'RndUp' ; Dscr: 'away from zero') );
The above are from my module DecimalRounding_JH1.
And then there is "statistical rounding" which is not
included in the current DecimalRounding_JH1.
> By default, the RoundTo() method does bankers rounding
> but that can be changed via the SetRoundMode() function.
I would not say that RoundTo() *does* bankers rounding.
The D7 help says that RoundTo() *uses* bankers rounding,
which is technically true; but the results are not what
the innocent programmer would expect to see. Instead,
it gives this:
RoundTo(1.235,-2) ==> 1.24
RoundTo(1.245,-2) ==> 1.25
If you examine more closely the real numbers being passed
into and out of the RoundTo function, you will observe for
RoundingMode: rmNearest
RoundTo(1.235,-2) ==> 1.24
Exact Inp: + 1.23500 00000 00000 09769 96261 67013
77555 72795 86791 99218 75
Exact Out: + 1.23999 99999 99999 99111 82158 02998
74767 66109 46655 27343 75
RoundTo(1.245,-2) ==> 1.25
Exact Inp: + 1.24500 00000 00000 10658 14103 64015
02788 06686 40136 71875
Exact Out: + 1.25
Here is the code that I used for the test:
Uses Math, ExactFloatToStr_JH0;
procedure TForm1.Button1Click(Sender: TObject);
{sub}procedure TestRT(const AValue: Double; const ADigit:
TRoundToRange);
var AVStr, RVStr: string; RVal: double;
begin
RVal := RoundTo(AValue,ADigit);
Memo1.Lines.Add(Format(' RoundTo(%S,%D) ==> %S',
[FloatToStr(AValue),ADigit,FloatToStr(RVal)]));
AVStr := ExactFloatToStrEx(AValue);
Memo1.Lines.Add(Format(' ExactInp: %S',[AVStr]));
RVStr := ExactFloatToStrEx(RVal);
Memo1.Lines.Add(Format(' ExactOut: %S',[RVStr]))
end;
var ORM, RM: TFPURoundingMode;
const RoundingModeStrs: array [TFPURoundingMode] of string =
('rmNearest','rmDown','rmUp','rmTruncate');
begin
For RM := rmNearest to rmTruncate do begin
ORM := SetRoundMode(RM);
Memo1.Lines.Add('RoundingMode: '+RoundingModeStrs[RM]);
Try
TestRT(1.235, -2); // 1.24
TestRT(1.245, -2); // 1.24
Finally
SetRoundMode(ORM);
End;
end;
end;
I will post the source for the whole test if anyone is interested.
Rgds, JohnH
--
Support the movement to add floating and fixed
decimal fraction numbers to Delphi.
http://qc.borland.com/wc/qcmain.aspx?d=28022
DecimalRounding (JH1)
http://cc.codegear.com/Item.aspx?id=21909
IEEE Number Analyzer (project code)
http://cc.codegear.com/item.aspx?id=23631
ExactFloatToStr
http://cc.codegear.com/Item.aspx?id=19421
> No, it is fully documented "Bankers" rounding. Rounding
> is to the nearest *even* value.
Wayne,
Then why does SimpleRoundTo give the following?
SimpleRoundTo(1.245,-2) ==> 1.25
Exact Inp: + 1.24500 00000 00000 10658 14103 64015 02788 06686
40136 71875
Exact Out: + 1.25
At least for D7, SimpleRoundTo was screwed up. See
open QualityCentral reports #8070 and #8143:
_RoundTo and SimpleRoundTo are very sick_
http://qc.codegear.com/wc/qcmain.aspx?d=8070
_Replace RoundTo and SimpleRoundTo_
http://qc.codegear.com/wc/qcmain.aspx?d=8143
Rgds, JohnH
> That is correct -- that is the IEEE spec, as I understand it.
Nick,
So what about the fact that for D7,
SimpleRoundTo(79.605,-2) ==> 79.61
ExactInp: + 79.60500 00000 00003 97903 93202 56561 04087 82958
98437 5
ExactOut: + 79.60999 99999 99999 43156 58113 91919 85130 31005
85937 5
Rgds, JohnH
Ralf,
According to
http://www.diycalculator.com/popup-m-round.shtml
"arithmetic rounding" is half-up toward positive for positive
numbers.
So how do you explain this fact:
SimpleRoundTo(79.615,-2) ==> 79.61
ExactInp: + 79.61499 99999 99994 88409 23025 27278 66172 79052
73437 5
We are damn slow learners. We would rather argue over
some silly point that change the environment to eliminate
the problem. --JohnH
If you ask me i would descibe Arithmetic rounding as 'to nearest or if
undecidable away from zero'. So correctly named SimpleRoundTo() should do
Symmetric Arithmetic Rounding. I think you called it HalfUp in your replacement
routines.
>
> So how do you explain this fact:
> SimpleRoundTo(79.615,-2) ==> 79.61
> ExactInp: + 79.61499 99999 99994 88409 23025 27278 66172 79052
> 73437 5
> ExactOut: + 79.60999 99999 99999 43156 58113 91919 85130 31005
> 85937 5
>
> Rgds, JohnH
>
I don't know what you want to hear from me, John? I'm pretty shure you know why
this is happening.
The problem isn't the Algorithm(if we had real decimal ALU's and proper
datatypes for that it would work) but the used datatype. It was a dumb idea from
Borland/Codegear to put rounding functions in math.pas without applying some
kind of delta to the input values to adjust the inherently missing precision of
binary interpreted decimal values. They should at least have mentioned the
implications in the help files.
Neither do i.
> What is "arithmetic rounding"?
> The types that I am familiar with include:
> (Abbr: 'HalfEven'; Dscr: 'to nearest or even (bankers)'),
> (Abbr: 'HalfPos' ; Dscr: 'to nearest or toward positive'),
> (Abbr: 'HalfNeg' ; Dscr: 'to nearest or toward negative'),
> (Abbr: 'HalfDown'; Dscr: 'to nearest or toward zero'),
> (Abbr: 'HalfUp' ; Dscr: 'to nearest or away from zero'),
> (Abbr: 'RndNeg' ; Dscr: 'toward negative (floor)'),
> (Abbr: 'RndPos' ; Dscr: 'toward positive (ceil )'),
> (Abbr: 'RndDown' ; Dscr: 'toward zero (trunc)'),
> (Abbr: 'RndUp' ; Dscr: 'away from zero') );
> The above are from my module DecimalRounding_JH1.
> And then there is "statistical rounding" which is not
> included in the current DecimalRounding_JH1.
>
HalfUp
>> By default, the RoundTo() method does bankers rounding
>> but that can be changed via the SetRoundMode() function.
>
> I would not say that RoundTo() *does* bankers rounding.
> The D7 help says that RoundTo() *uses* bankers rounding,
> which is technically true; but the results are not what
> the innocent programmer would expect to see. Instead,
> it gives this:
> RoundTo(1.235,-2) ==> 1.24
> RoundTo(1.245,-2) ==> 1.25
>
Ok. RoundTo() *tries* to use bankers rounding.
But because of the missing precision in the given float value
it looks different (to the innocent).
Ralf, et. al, Thanks for suffering my outrage. I
think that to properly fix the problem would require a
lot of real work and I guess that the "bean counters",
as we used to call them, were not convinced by the
proponents of the fix of the worthwhileness of the
expenditure. Regards, JohnH
--
Support the movement to add floating and fixed
decimal fraction numbers to Delphi.
http://qc.borland.com/wc/qcmain.aspx?d=28022
DecimalRounding (JH1)
http://cc.codegear.com/Item.aspx?id=21909
IEEE Number Analyzer (project source code)
| I will post the source for the whole test if anyone is interested.
I am interested. I'm leaving in a couple of hours for a mini-vacation
so won't be able to see it for a week. If no one else is interested
you can e-mail me.
--
Q
08/05/2007 07:55:11
> | I will post the source for the whole test if anyone is
interested.
> I am interested.
I just posted it in b.p.attachments group under subject:
T_RoundTo_3 -- Test for bug in RoundTo functions
--JohnH
I was curious to look at the source code of SimpleRoundTo in math.pas
(D2007).
Here it is - original code:
function SimpleRoundTo(const AValue: Double; const ADigit: TRoundToRange
= -2): Double;
var
LFactor: Double;
begin
LFactor := IntPower(10, ADigit);
if AValue < 0 then
Result := Trunc((AValue / LFactor) - 0.5) * LFactor
else
Result := Trunc((AValue / LFactor) + 0.5) * LFactor;
end;
I wanted to find where the problem occurs so copied it and modified it
to following code:
function SimpleRoundTo(const AValue: Double; const ADigit: TRoundToRange
= -2): Double;
var
LFactor: Double;
begin
LFactor := IntPower(10, ADigit);
if AValue < 0 then
Result := Trunc((AValue / LFactor) - 0.5) * LFactor
else
Begin
//Result := Trunc((AValue / LFactor) + 0.5) * LFactor;
//******
Result := (AValue / LFactor) + 0.5;
Result := Trunc(Result);
Result := Result * LFactor;
//******
End;
end;
I just split single line expression into three lines, and surprisingly
error disappeared?! WTF???
I looked into CPU window there are differences but I'm not very familiar
with opcodes - analyzing what exactly happened would probably take me a
day or so nad I still might get it wrong. Anybody care to
comment/explain what and why happened here?
Dragan
> I just split single line expression into three lines, and surprisingly
> error disappeared?! WTF???
It is almost certainly a change in underlying type resulting from the
breaking up of the expression that has resulted in the different result,
especially if (as seems to be the case) the original "problem" stems
from an internal representation issue in some floating point type.
i.e. one value that cannot be exactly represented in a double variable
may be representable in an extended variable or vice versa.
The point at which a piece of code incurs a change from one internal
representation to another will determine where/when/if representation
errors creep in. Changing the point(s) at which representation changes
can change the result of an otherwise equivalent calculation.
In this case, in the original code, the expression (AValue / LFactor) +
0.5 yields an Extended result (this is defined by the language - the /
operator yields Extended).
In your re-written version that Extended result is assigned to a Double,
invoking a representation change.
That Double value is then converted BACK to an Extended value for
passing to the Trunc function.
In the original version this flip-flopping between Extended and Double
does not take place. The expression is evaluated and passed to Trunc()
in Extended representation and the result only converted to Double once
fully evaluated.
That probably explains the difference.
--
JS
TWorld.Create.Free;
seems that you're right introducing tmp variable of Extended type realy
shows that problem is with TRUNC function, modified test code looks
like this :
//test call: SimpleRoundTo(79.625, -2);
function SimpleRoundTo(const AValue: Double; const ADigit: TRoundToRange
= -2): Double;
var
TmpExt : Extended;
LFactor: Double;
begin
LFactor := IntPower(10, ADigit);
if AValue < 0 then
Result := Trunc((AValue / LFactor) - 0.5) * LFactor
else
Begin
//Result := Trunc((AValue / LFactor) + 0.5) * LFactor;
//******
TmpExt := (AValue / LFactor) + 0.5;
TmpExt := Trunc(TmpExt); //returns 7962 instead of 7963
Result := TmpExt * LFactor;
//******
End;
To Nick Hodges: context sensitive for Trunc function (amongs others) is
still missing, broken help is my biggest gripe with D2007. I still keep
D6 installed as a help reference when D2007 help fails.
> No, it is fully documented "Bankers" rounding. Rounding is to the
> nearest even value.
But that's not what the D2006 Help says about SimpleRoundTo:
"SimpleRoundTo uses asymmetric arithmetic rounding to determine how to
round values that are exactly midway between the two values that have
the desired number of significant digits. This method always rounds to
the larger value."
But then the examples in the Help are meaningless and don't support
neither the above claim nor Banker's Rounding.
Looking at the source, it definitely is trying to do something else
than Banker's Rounding. It also seems the part "This method always
rounds to the larger value." actually should read "This method always
rounds to the larger *absolute* value."
All that aside from the fact that it doesn't work...
--
Anders Isaksson, Sweden
BlockCAD: http://web.telia.com/~u16122508/proglego.htm
Gallery: http://web.telia.com/~u16122508/gallery/index.htm
> i.e. one value that cannot be exactly represented in a double
> variable may be representable in an extended variable or vice versa.
Or more probable: The value, as represented in a double might be
*slightly* larger than the original value, while represented as an
extended it might be *slightly* smaller (or vice versa).
"Anders Isaksson" <i.rather@not> wrote
> But that's not what the D2006 Help says about
> SimpleRoundTo:
> "SimpleRoundTo uses asymmetric arithmetic rounding
> to determine how to round values that are exactly
> midway between the two values that have the desired
> number of significant digits. This method always
> rounds to the larger value."
"To the larger value" to me would imply that
-1.5 would go to -2, and +1.5 would go to +2.
To me, that would be symmetric, not asymmetric.
But I have some doubt. Am I wrong? The Help
writers could have made written this more clearly.
Given, our present IQs there is no such thing as
"simple rounding".
Regards, JohnH
> Rod --
>
> It's in our internal system -- and has been "bandied around" a bit.
> I've raised its visibility and it will get looked at afresh.
>
> --
Whilst I wouldn't recommend my solution as a CodeGear fix, you may
consider how I solved the problem for the time being. I've written a new
function RoundMoney, which does what I wanted SimpleRoundTo to do:
function RoundMoney(money : double) : double ;
var
precision : string ;
tempStr : string ;
begin
precision := '%.' + IntToStr(CurrencyDecimals) + 'f' ;
tempStr := Format(precision, [Abs(money + (Sign(money) * 0.00000001))])
;
result := Sign(money) * StrToFloatDef(tempStr, 0) ;
end ;
You might also note that this function ALWAYS rounds away from zero, so
-1.625 rounds to -1.63, not -1.62.
This is done so that (for instance) credit notes generate an equal and
opposite financial value from the original invoice. Otherwise you'd end
up with a penny/cent/yourcurrency100th discrepancy.
Trevor
> A good programmer should know that there is no such
> thing as "simple rounding"
I disagree.
Ask any mathematician and you'll get the same answer: 0.5 rounds up.
Check it on your calculator too.
Mathematically, this is:
0 <= x < 0.5 => 0 (or [0, 0.5[ => 0)
0.5 <= x < 1.0 => 1 (or [0.5, 1.0[ => 1)
The only area of debate is whether negative values round towards or away
from zero.
Banker's Rounding is a convenience to avoid either party (buyer or seller)
gaining a financial advantage from fractional values.
> That aside, when ordinary programmers get caught in
> the trap of using floating *binary* point numbers,
> to represent (fixed or floating) decimal fraction
> numbers and have to do any kind of rounding, then
> they stumble all over themselves.
This isn't a trap that can always be avoided. You may be given a database
populated with floating point values that are not 100% accurate. Any
reasonable programmer should be able to work around this without recourse
to scaled integer values or BCD representation, armed only with the
knowledge of the required precision and values that conform to that
precision.
Of course, using scaled integers or BCD does make it easier, but don't
yourself fall into the trap of believing that this is the only solution.
> I am really disappointed that I am still reading
> such ill informed discussion of this problem
Not ill-informed at all - and rather superior of you to suggest so. My
original post (if you refer back) was noting a failure of SimpleRoundTo()
to conform to its specification. The fact that it is being used to handle
currency is neither a mistake nor necessarily bad practice. Your library
routines may well make life easier for some, and indeed maybe for me too.
However, there are times and circumstances which do make and will
continue to make situations such as mine unavoidable. Read Kurt Godel's
hypothesis for a better understanding of why this is so.
Trevor
> function RoundMoney(money : double) : double ;
> var precision : string ; tempStr : string ;
> begin
> precision := '%.' + IntToStr(CurrencyDecimals) + 'f' ;
> tempStr := Format(precision,
> [Abs(money + (Sign(money) * 0.00000001))]) ;
> result := Sign(money) * StrToFloatDef(tempStr, 0) ;
> end ;
Trevor, You may wish to note how I modified your code to
avoid the conversion to string and back. You may also
wish to note how the modification stretches the range
of correct operation. You can find the source for a project
with both the modification and the tests in the attachments
group (borland.public.attachments) with the subject
T_RoundTo_3. Regards, JohnH
Hogwash! Furthermore, that statement is not even
clear as to whether "rounds up" means away from
zero or toward plus infinity. --JohnH
> "Rudy Velthuis [TeamB]" wrote
> >
> > For most people it is pretty clear: for positive numbers, rounding
> > up means towards plus infinity AND away from zero.
>
> John was pointing out that for negative numbers there's no such
> universal agreement about which way is 'up'. Surely a round function
> should work with negatives.
I am aware of that. <g>
--
Rudy Velthuis [TeamB] http://rvelthuis.de
"If it wasn't for muscle spasms, I wouldn't get any exercise at
all."
For most people it is pretty clear: for positive numbers, rounding up
means towards plus infinity AND away from zero.
--
Rudy Velthuis [TeamB] http://rvelthuis.de
"A child of five could understand this. Fetch me a child of five."
-- Groucho Marx
John was pointing out that for negative numbers there's no such universal
agreement about which way is 'up'. Surely a round function should work with
negatives.
re : "Check it on your calculator too."
I'm still trying to find the 'Round' button on my calculator ... <g>
bobD
I would say they simply forget to update the help after D7.
In D7 it was asymmetric and described correctly as such in the help. (For
example SimpleRoundTo(-1.235, -2) = -1.23) . But they changed the implementation
to symmetric later on.
I would love to sneak into their internal bugtracking system. I bet there is a
report from someone who had problems to distinguish between symmetric/asymmetric
rounding and found someone *fixing* it.
It's next to the 'any' key...
David Erbas-White
> I'm still trying to find the 'Round' button on my calculator ... <g>
>
<g> Well, I must admit that all mine are square - I should have checked
first ;-)
Trevor
> Trevor, You may wish to note how I modified your code to
> avoid the conversion to string and back. You may also
> wish to note how the modification stretches the range
> of correct operation. You can find the source for a project
> with both the modification and the tests in the attachments
> group (borland.public.attachments) with the subject
> T_RoundTo_3. Regards, JohnH
>
Thanks John - I'll grab it now!
Trevor
> Hogwash! Furthermore, that statement is not even
> clear as to whether "rounds up" means away from
> zero or toward plus infinity. --JohnH
>
Hey, I was using everyday language! The example I gave was the positive
value 0.5, and I did say "round up", i.e. "towards plus infinity" (and I
presume you mean "countable infinity" :-) )
I *also* said that the only dispute, therefore, was what happens to
negative values - do they round towards or away from zero. Was that not
clear?
So we're actually saying the same thing; in which case hopefully not
"hogwash".
Trevor
> Trevor, You may wish to note how I modified your code to
> avoid the conversion to string and back. You may also
> wish to note how the modification stretches the range
> of correct operation. You can find the source for a project
> with both the modification and the tests in the attachments
> group (borland.public.attachments) with the subject
> T_RoundTo_3. Regards, JohnH
Your code is perfect, thanks, and has now replaced my clunky quick hack
fix!
Trevor
I have a problem understanding what is meant by "symmetric"
versus "asymetric" rounding? Intuitively I would expect
"asymetric" rounding to be a synonym for "banker's" rounding
(which is well defined) but that is not what the documentation
seems to imply.
However, there must be an error in the documentation on
"SimpleRoundTo" in my BDS2006 help files:
Does SimpleRoundTo(1234567, 3) really return 1234000 such
as the help example says? I certainly hope not!
Sven
Me too! I guess that symmetric means that the
rounding rules act symmetrically about zero. In
other words, if 1.1 rounds to 2 then -1.1 rounds to -2.
I think that about half of our rounding problems are
sloppy terminology. The following site seems to be
pretty good at explaining some most of the common
rounding rules:
http://www.diycalculator.com/popup-m-round.shtml
> Does SimpleRoundTo(1234567, 3) really return 1234000 such
> as the help example says? I certainly hope not!
With D7:
SimpleRoundTo(1234567,3) ==> 1235000
Rgds, JohnH
"Trevor Toms" <sm...@cix.compulink.co.uk> wrote
> Your code is perfect, thanks, and has now replaced ...
Trevor, Please check my code with more complete
examination and testing, particularly for actions on
negative numbers. My tests were mainly to quickly
find suspected problems, not to show that there are
no problems. Regards, JohnH
> Ask any mathematician and you'll get the same answer: 0.5 rounds up.
That I doubt. This produces a biased rounding - round a lot of positive
values and add them, and the result will consistently be larger than
the sum of the original values.
Bankers rounding is statistically unbiased - round a lot of positive
values and add them, and the result will be very much the same as the
sum of the original values.
> That I doubt.
Please refer to the rest of my post. Banker's Rounding is a very specific
method of rounding that (as you note) avoids financial bias since money
comes in discrete whole values.
What we're dealing with in my original context though is slightly
different, since the values being managed are not necessarily discrete.
Also, I'm not concerned about bias - I want consistency and accuracy. I
don't want "some" to be rounded up and "others" to be rounded down.
SimpleRoundTo doesn't conform to its specification - and that's all I'm
trying to achieve! If the specification said "Uses Banker's Rounding" I
wouldn't have used it. JohnH's solution appears to be my best option for
now.
Trevor
> *From:* "John Herbster" <herb-sci1_AT_sbcglobal.net>
> *Date:* Tue, 7 Aug 2007 08:02:38 -0500
> Trevor, Please check my code with more complete
> examination and testing, particularly for actions on
> negative numbers. My tests were mainly to quickly
> find suspected problems, not to show that there are
> no problems. Regards, JohnH
>
Will do - thanks.
Trevor
Trevor,
By that, I am afraid that you are referring to my
modification of the code
> precision := '%.' + IntToStr(CurrencyDecimals) + 'f' ;
> tempStr := Format(precision,
> [Abs(money + (Sign(money) * 0.00000001))]) ;
> result := Sign(money) * StrToFloatDef(tempStr, 0) ;
I believe that a better solution would be to use one
of the rounding functions from my decimal rounding
module available from CodeCentral, using rounding
control word drHalfUp, while also paying attention
to the expected precision of your variables. The
routines in this unit do more intelligent selection
of your "0.00000001" fudge factor.
Regards, JohnH
--
Support the movement to add floating and fixed
decimal fraction numbers to Delphi.
http://qc.borland.com/wc/qcmain.aspx?d=28022
DecimalRounding (JH1)
http://cc.codegear.com/Item.aspx?id=21909
IEEE Number Analyzer (project code)
http://cc.codegear.com/item.aspx?id=23631
ExactFloatToStr
http://cc.codegear.com/Item.aspx?id=19421
> *From:* "John Herbster" <herb-sci1_AT_sbcglobal.net>
> *Date:* Thu, 9 Aug 2007 06:19:26 -0500
>
> I believe that a better solution would be to use one
> of the rounding functions from my decimal rounding
> module available from CodeCentral, using rounding
> control word drHalfUp, while also paying attention
> to the expected precision of your variables. The
> routines in this unit do more intelligent selection
> of your "0.00000001" fudge factor.
>
> Regards, JohnH
>
Thanks John. The 0.0000001 was indeed a kludge! I'll look into your other
routines over the weekend.
Trevor
Changing the declaration of LFactor in the Delphi code to extended seems
to help. I'd suggest changing all the doubles in the routine to
extendeds. Of course, this won't help if the string doesn't have an
exact binary representation and the binary representation is on the
other side of the 5 threshold than the string, but I wonder if it will
make the routine work properly when the string does have such a
representation.
--
David Marcus
| I just posted it in b.p.attachments group under subject:
| T_RoundTo_3 -- Test for bug in RoundTo functions
Snagged it.
Thanks!!!
--
Q
08/10/2007 15:43:52
XanaNews Version 1.17.5.7 [Q's salutation mod]
"David Marcus" <David...@AlumDotMIT.edu> wrote
> Changing the declaration of LFactor in the Delphi
> code to extended seems to help. I'd suggest
> changing all the doubles in the routine to
> extendeds. Of course, this won't help if the
> string doesn't have an exact binary representation
David, Most do not!
> and the binary representation is on the other side
> of the 5 threshold than the string, but I wonder
> if it will make the routine work properly when the
> string does have such a representation.
You can save yourself a lot of wondering by properly
using the rounding functions in DecimalRounding_JH1.
Rgds, JohnH
I'm sure the routines are useful, but my point was that the Delphi
routine seems to have a bug. There are two separate issues: conversion
from decimal strings to binary floating point and rounding of binary
floating point numbers.
IEEE arithmetic works pretty well, in general. But, trying to round a
floating point number by using doubles is not a good idea.
If you really want to round a decimal string, then converting it to a
binary floating point number is not a good idea. But, if you want to
round a binary floating point number, then a simple fix will make the
Delphi routine do it much more reliably.
--
David Marcus
Note that Delphi's writeln function does rounding and doesn't have the
problem that SimpleRoundTo has. E.g.,
var
X: double;
begin
X := 79.625;
writeln( 79.625:5:2 );
writeln( X:5:2 );
end.
produces
79.63
79.63
So, if writeln can do it, then SimpleRoundTo should be able to do it,
too.
--
David Marcus
"David Marcus" <David...@AlumDotMIT.edu> wrote
> I'm sure the routines are useful, but my point
> was that the Delphi routine seems to have a bug.
There is no doubt about that.
> There are two separate issues:
David,
> conversion from decimal strings to binary floating
> point
What do you see as the problem above? Maybe the
fact that most decimal fractions have no exact
representation in floating binary point
variables or something else?
> and rounding of binary floating point numbers [to
> fixed decimal fraction]
> IEEE arithmetic works pretty well, in general. But,
> trying to round a floating point number by using
> doubles is not a good idea.
Then why do we do so often? Is it perhaps because
there is not a good alternative?
> If you really want to round a decimal string, then
> converting it to a binary floating point number
> is not a good idea.
> But, if you want to round a binary floating point
> number, then a simple fix will make the Delphi
> routine do it much more reliably.
A proof would be welcome. Please include a statement
of the rounding rule along with the code and test
results.
Regards, JohnH
Yes. However, whether it is a problem, depends on the application.
> > and rounding of binary floating point numbers [to
> > fixed decimal fraction]
>
> > IEEE arithmetic works pretty well, in general. But,
> > trying to round a floating point number by using
> > doubles is not a good idea.
>
> Then why do we do so often? Is it perhaps because
> there is not a good alternative?
Sorry, but I don't know what you mean. I don't do rounding by doing
arithmetic with doubles. The best way to round numbers for output is to
use write or writeln. If you need to round a number to store it into a
variable, then Delphi doesn't make this easy.
> > If you really want to round a decimal string, then
> > converting it to a binary floating point number
> > is not a good idea.
>
> > But, if you want to round a binary floating point
> > number, then a simple fix will make the Delphi
> > routine do it much more reliably.
>
> A proof would be welcome. Please include a statement
> of the rounding rule along with the code and test
> results.
{$Optimization on}
{$apptype console}
program Test;
uses
Math;
function F(const AValue: extended; const ADigit: TRoundToRange = -2):
extended;
var
LFactor: extended;
begin
LFactor := IntPower(10, ADigit);
if AValue < 0 then
Result := Trunc((AValue / LFactor) - 0.5) * LFactor
else
Result := Trunc((AValue / LFactor) + 0.5) * LFactor;
end;
var
J, K, L: integer;
X, Y: extended;
begin
for J := 0 to 9 do
for K := 0 to 9 do
for L := 0 to 9 do begin
X := ( J / 10 + K / 100 );
Y := X + L / 1000;
if L >= 5 then
X := X + 1 / 100;
if abs( F( Y, -2 ) - X ) > 0.005 then
writeln( '0.', J, K, L, ' ', Y, F( Y, -2 ), Y:5:2 );
end;
writeln( 'SimpleRoundTo' );
for J := 0 to 9 do
for K := 0 to 9 do
for L := 0 to 9 do begin
X := ( J / 10 + K / 100 );
Y := X + L / 1000;
if L >= 5 then
X := X + 1 / 100;
if abs( SimpleRoundTo( Y, -2 ) - X ) > 0.005 then
writeln( '0.', J, K, L, ' ', Y, F( Y, -2 ), Y:5:2 );
end;
end. // Test.
0.135 1.35000000000000E-0001 1.30000000000000E-0001 0.14
0.535 5.35000000000000E-0001 5.30000000000000E-0001 0.54
SimpleRoundTo
0.015 1.50000000000000E-0002 2.00000000000000E-0002 0.02
0.045 4.50000000000000E-0002 5.00000000000000E-0002 0.05
0.055 5.50000000000000E-0002 6.00000000000000E-0002 0.06
0.075 7.50000000000000E-0002 8.00000000000000E-0002 0.08
0.095 9.50000000000000E-0002 1.00000000000000E-0001 0.10
0.105 1.05000000000000E-0001 1.10000000000000E-0001 0.11
0.125 1.25000000000000E-0001 1.30000000000000E-0001 0.13
0.145 1.45000000000000E-0001 1.50000000000000E-0001 0.15
0.155 1.55000000000000E-0001 1.60000000000000E-0001 0.16
0.175 1.75000000000000E-0001 1.80000000000000E-0001 0.18
0.185 1.85000000000000E-0001 1.90000000000000E-0001 0.19
0.205 2.05000000000000E-0001 2.10000000000000E-0001 0.21
0.215 2.15000000000000E-0001 2.20000000000000E-0001 0.22
0.235 2.35000000000000E-0001 2.40000000000000E-0001 0.24
0.245 2.45000000000000E-0001 2.50000000000000E-0001 0.25
0.255 2.55000000000000E-0001 2.60000000000000E-0001 0.26
0.285 2.85000000000000E-0001 2.90000000000000E-0001 0.29
0.295 2.95000000000000E-0001 3.00000000000000E-0001 0.30
0.305 3.05000000000000E-0001 3.10000000000000E-0001 0.31
0.315 3.15000000000000E-0001 3.20000000000000E-0001 0.32
0.345 3.45000000000000E-0001 3.50000000000000E-0001 0.35
0.355 3.55000000000000E-0001 3.60000000000000E-0001 0.36
0.365 3.65000000000000E-0001 3.70000000000000E-0001 0.37
0.375 3.75000000000000E-0001 3.80000000000000E-0001 0.38
0.415 4.15000000000000E-0001 4.20000000000000E-0001 0.42
0.425 4.25000000000000E-0001 4.30000000000000E-0001 0.43
0.435 4.35000000000000E-0001 4.40000000000000E-0001 0.44
0.445 4.45000000000000E-0001 4.50000000000000E-0001 0.45
0.475 4.75000000000000E-0001 4.80000000000000E-0001 0.48
0.485 4.85000000000000E-0001 4.90000000000000E-0001 0.49
0.495 4.95000000000000E-0001 5.00000000000000E-0001 0.50
0.505 5.05000000000000E-0001 5.10000000000000E-0001 0.51
0.565 5.65000000000000E-0001 5.70000000000000E-0001 0.57
0.575 5.75000000000000E-0001 5.80000000000000E-0001 0.58
0.585 5.85000000000000E-0001 5.90000000000000E-0001 0.59
0.595 5.95000000000000E-0001 6.00000000000000E-0001 0.60
0.605 6.05000000000000E-0001 6.10000000000000E-0001 0.61
0.615 6.15000000000000E-0001 6.20000000000000E-0001 0.62
0.625 6.25000000000000E-0001 6.30000000000000E-0001 0.63
0.635 6.35000000000000E-0001 6.40000000000000E-0001 0.64
0.695 6.95000000000000E-0001 7.00000000000000E-0001 0.70
0.705 7.05000000000000E-0001 7.10000000000000E-0001 0.71
0.715 7.15000000000000E-0001 7.20000000000000E-0001 0.72
0.725 7.25000000000000E-0001 7.30000000000000E-0001 0.73
0.735 7.35000000000000E-0001 7.40000000000000E-0001 0.74
0.745 7.45000000000000E-0001 7.50000000000000E-0001 0.75
0.755 7.55000000000000E-0001 7.60000000000000E-0001 0.76
0.765 7.65000000000000E-0001 7.70000000000000E-0001 0.77
0.815 8.15000000000000E-0001 8.20000000000000E-0001 0.82
0.825 8.25000000000000E-0001 8.30000000000000E-0001 0.83
0.835 8.35000000000000E-0001 8.40000000000000E-0001 0.84
0.845 8.45000000000000E-0001 8.50000000000000E-0001 0.85
0.855 8.55000000000000E-0001 8.60000000000000E-0001 0.86
0.865 8.65000000000000E-0001 8.70000000000000E-0001 0.87
0.875 8.75000000000000E-0001 8.80000000000000E-0001 0.88
0.885 8.85000000000000E-0001 8.90000000000000E-0001 0.89
0.895 8.95000000000000E-0001 9.00000000000000E-0001 0.90
0.945 9.45000000000000E-0001 9.50000000000000E-0001 0.95
0.955 9.55000000000000E-0001 9.60000000000000E-0001 0.96
0.965 9.65000000000000E-0001 9.70000000000000E-0001 0.97
0.975 9.75000000000000E-0001 9.80000000000000E-0001 0.98
0.985 9.85000000000000E-0001 9.90000000000000E-0001 0.99
0.995 9.95000000000000E-0001 1.00000000000000E+0000 1.00
This is just a crude test, but it provides evidence for two conclusions:
1. SimpleRoundTo's use of "double" instead of "extended" is a bug.
2. The rounding routines that writeln uses are superior.
Two other facts that are relevant:
3. Rounding by rescaling (as SimpleRoundTo or my fixed version does)
makes you susceptible to loss of precision.
4. Conversion of decimal strings to binary floating point is rarely
exact.
--
David Marcus
Do you mean that you would use Write() or WriteLn()
to round a number stored in a floating binary point
variable to a string format? If so how do you spec
what kind of rounding to do?
> If you need to round a number to store it into a
> variable, then Delphi doesn't make this easy.
In my opinion, Delphi makes it too easy. Programmers
are frequently using StrToFloat() and assignments like
<FP var> := <currency var>; or
<FP var> := <numerator>/<denominator>; or
<double var> := <extended var>
without understand what rounding or loss of precision
or data is happening.
> > > But, if you want to round a binary floating point
> > > number, then a simple fix will make the Delphi
> > > routine do it much more reliably.
> > A proof would be welcome. Please include a statement
> > of the rounding rule along with the code and test
> > results.
> program Test;
> ...
David,
Thanks I will check out your proof and respond later.
Regards, JohnH
I think this statement I made is premature. All my little test shows is
that writeln is consistent: when you ask it round the number, the result
is consistent with what you get when you ask writeln for all digits.
We'd have to examine the binary value to see if writeln is rounding
correctly.
--
David Marcus
Sure.
> If so how do you spec what kind of rounding to do?
Are you asking whether writeln allows you to configure the type of
rounding? It doesn't. It rounds to nearest and rounds numbers that are
exactly in the middle away from zero.
> > If you need to round a number to store it into a
> > variable, then Delphi doesn't make this easy.
>
> In my opinion, Delphi makes it too easy. Programmers
> are frequently using StrToFloat() and assignments like
> <FP var> := <currency var>; or
> <FP var> := <numerator>/<denominator>; or
> <double var> := <extended var>
> without understand what rounding or loss of precision
> or data is happening.
None of these does what I was referring to. I was referring to rounding
an extended value to a specified decimal precision and storing the
result in an extended variable.
--
David Marcus
> function F (
> const AValue: extended;
> const ADigit: TRoundToRange = -2): extended;
> var LFactor: extended;
> begin
> LFactor := IntPower(10, ADigit);
> if AValue < 0 then
> Result := Trunc((AValue / LFactor) - 0.5) * LFactor
> else
> Result := Trunc((AValue / LFactor) + 0.5) * LFactor;
> end;
> 1. SimpleRoundTo's use of "double" instead of
> "extended" is a bug.
David, Yes, it should have been an extended type.
I would call this a case of thoughtlessness.
Looking again at the D7 Help for SimpleRoundTo, it
claims that it "Rounds a floating-point value to a
specified digit or power of ten using asymmetric
arithmetic rounding." From my Googling, I believe
that asymmetric arithmetic rounding normal means
that rounding should be to the nearest and points
midway between are suppose to round toward plus inf.
> 2. The rounding routines that writeln uses are
> superior.
Superior -- in what regard? I have never seen a
statement of what rounding rule that they are
supposed to follow.
> Two other facts that are relevant:
> 3. Rounding by rescaling (as SimpleRoundTo or my
> fixed version does) makes you susceptible to loss
> of precision.
All rounding makes you susceptible to loss of
precision.
> 4. Conversion of decimal strings to binary floating
> point is rarely exact.
I note that in your test you are using WriteLn() to
give you strings with numbers like
1.35000000000000E-0001
Are you aware that these are not exact? The exact
number would be something like
0.13499 99999 99999 99999 13263 82620 11596
45279 40377 59304 04663 08593 75
If you really want to know the exact value, you should
be using something like my ExactFloatToStr() routines
that are available from CodeCentral.
You might want to check some of the small differences
between you F function and my DecimalRoundDbl function
(available from CodeCentral as part of the
DecimalRounding_JH1 module.)
When rounding the nominally "0.045" value
Inp=+ 0.04499 99999 99999 99999 82381 71469 71105
52947 37889 19858 63447 18933 10546 875
Your code gives
DsF=+ 0.04999 99999 99999 99999 72894 94568
78623 89149 81367 99782 51457 21435 54687 5
And DecimalRoundDbl gives
DRD=+ 0.05000 00000 00000 00000 06776 26357
80344 02712 54658 00054 37135 69641 11328 125
Yours is close, but not as close as it could be, given
that it is returning an extended result.
Many bugs are due to thoughtlessness.
> > 2. The rounding routines that writeln uses are
> > superior.
>
> Superior -- in what regard?
I meant superior to the fixed SimpleRoundTo, but I'll retract this
statement, since I jumped to a conclusion. It is certainly superior to
the bugged SimpleRoundTo.
> I have never seen a
> statement of what rounding rule that they are
> supposed to follow.
It appears to be doing round to nearest with numbers exactly in the
middle rounded away from zero. All the Fortran compilers I've used do
the same. It is too long since I've used a C compiler for me to
remember, but I suspect they do the same.
> > 3. Rounding by rescaling (as SimpleRoundTo or my
> > fixed version does) makes you susceptible to loss
> > of precision.
>
> All rounding makes you susceptible to loss of
> precision.
There is a well-defined mathematical function from real numbers to real
numbers that rounds numbers to a given number of digits. There is no
reason why a computer can't implement the restriction of this function
to the set of binary floating point numbers. My point was that
implementing such a function by rescaling is unlikely to give the
correct answer for all values in the domain.
> I note that in your test you are using WriteLn() to
> give you strings with numbers like
> 1.35000000000000E-0001
> Are you aware that these are not exact?
I knew they weren't exact, but I didn't remember until after my first
post that I don't know if writeln is rounding correctly.
--
David Marcus
Come to think of it, it might be better if a routine like writeln first
converts the value to decimal, then rounds it. This way, if you assign a
decimal value to a real variable and the binary value isn't identical to
the decimal value, you would still get what you expect when you write
the variable.
--
David Marcus
program Test;
var
X: extended;
J: integer;
begin
X := 0.49999999999999999;
writeln( X );
for J := 18 downto 1 do
writeln( X:J+3:J );
writeln( X:2:0 );
end.
Output:
5.00000000000000E-0001
0.499999999999999990
0.49999999999999999
0.5000000000000000
0.500000000000000
0.50000000000000
0.5000000000000
0.500000000000
0.50000000000
0.5000000000
0.500000000
0.50000000
0.5000000
0.500000
0.50000
0.5000
0.500
0.50
0.5
0
--
David Marcus
David, I am glad that you are interested in this
important stuff.
However, you need to give up on that WriteLn(X).
It is lying to you.
If your variable X above is of type extended, then
after the assignment, it will contain exactly
+0.49999 99999 99999 98999 82349 58821 22159
62812 47911 97478 77120 97167 96875
not 5.00000000000000E-0001
You can get the exact values with functions
in my ExactFloatToStr module which is available
from CodeCentral or you can write your own.
David, Please note that if you change your fucntion to
function DF2
(const AValue: extended;
const ADigit: TRoundToRange = -2)
: extended;
var LFactor: extended;
begin
LFactor := IntPower(10, -ADigit);
if AValue < 0
then Result := Trunc((AValue*LFactor) - 0.5)/LFactor
else Result := Trunc((AValue*LFactor) + 0.5)/LFactor;
end;
then the accuracy will be improved in a lot of cases.
Here are the results for an input of "0.045":
Inp=+ 0.04500 00000 00000 00000 16263 03258 72825
66510 11179 20130 49125 67138 67187 5
DsF=+ 0.04999 99999 99999 99999 72894 94568 78623
89149 81367 99782 51457 21435 54687 5
DF2=+ 0.05000 00000 00000 00000 06776 26357 80344
02712 54658 00054 37135 69641 11328 125
DRD=+ 0.05000 00000 00000 00000 06776 26357 80344
02712 54658 00054 37135 69641 11328 125
where DRD is from my DecimalRoundDouble function.
However the problem that most programmers run into
is still not fixed. Consider what will happen if the
input value is from a double precision variable, such
as might be retreived from a data table, instead of
an extended variable. Then you if you are rounding
"100000.015", what you will get is this:
Inp=+ 1 00000.01499 99999 99417 92339 08653 25927
73437 5
DF2=+ 1 00000.00999 99999 99998 01048 03398 71719
47956 08520 50781 25
DRD=+ 1 00000.02000 00000 00003 12638 80373 44440
81783 29467 77343 75
and you can see that again DRD is closer to the right
result. If you step through the DF2 calculation you
can see why. That is why my decimal rounding routines
use carefully chosen fudge factors.
Please note that even with your function F changed
to the following
> function DF2
> (const AValue: extended;
> const ADigit: TRoundToRange = -2)
> : extended;
> var LFactor: extended;
> begin
> LFactor := IntPower(10, -ADigit);
> if AValue < 0
> then Result := Trunc((AValue*LFactor) - 0.5)/LFactor
> else Result := Trunc((AValue*LFactor) + 0.5)/LFactor;
> end;
and even with the caller's input variable being of type
extended. It will still start giving bad outputs when
rounding test inputs around 9e12 to pennies as you
can see in the following:
Inp=+ 900 00000 00000.02499 96185 30273 4375
Dv2=+ 900 00000 00000.02000 04577 63671 875
DRD=+ 900 00000 00000.02999 97329 71191 40625
Regards, JohnH
Never said it was giving the exact value. Was just showing an example of
how writeln performs.
> If your variable X above is of type extended, then
> after the assignment, it will contain exactly
> +0.49999 99999 99999 98999 82349 58821 22159
> 62812 47911 97478 77120 97167 96875
> not 5.00000000000000E-0001
Which is the correctly rounded value, so writeln seems to be doing fine,
although one might ask why writeln doesn't give more digits by default.
--
David Marcus
Sure. If you assign to a double, you will lose precision. Binary
floating point is not decimal floating point. People who think it is,
will be surprised.
Whether 100000.02 is the "right" result depends on how you define the
problem. 100000.015 rounded to two digits is 100000.02, but Inp rounded
to two digits is 100000.01.
--
David Marcus
"David Marcus" <David...@AlumDotMIT.edu> wrote
> Whether 100000.02 is the "right" result depends
> on how you define the problem. 100000.015 rounded
> to two digits is 100000.02, but Inp rounded > to
> two digits is 100000.01.
Dear David,
The goal in writing my DecimalRounding package was to
help programmers having to use floating binary point
numbers for applications requiring decimal fraction
output. Copied here (from the beginning of that package)
is the problem as I see it and how that package solved
it.
"ROUTINES FOR ROUNDING IEEE-754 FLOATS TO SPECIFIED
NUMBER OF DECIMAL FRACTION DIGITS"
"Because, in general, numbers with decimal fractions
cannot be exactly represented in IEEE-754 floating
binary point variables, error limits are used to
determine if the input numbers are intended to
represent an exact decimal fraction rather than a
nearby value. Thus an error limit will be taken
into account when deciding that a number input such
as 1.295, which internally is represented as
1.29499 99999 …, really should be considered exactly
1.295 and that 0.29999 99999 ... really should be
interpreted as 0.3 when applying the rounding rules."
"These routines round input values to fit as closely
as possible to an output number with desired number
of decimal fraction digits. "
Good luck, JohnH
That's fine. But, not all people wanting to round a floating point
number have this goal. And, many people who get tangled up with this
problem are simply misusing floating point.
--
David Marcus