I need to convert a percentage (accurate to 2 digits) returned from an extension
to its integer equivalent (0-100). I naively was using int(), but observed the
behavior noted above. I realize this harks straight from C, so was wondering
what was the best Tcl solution (rounding is not acceptable).
Is it a matter of treating the returned percentage (e.g., .29) as a string and
grabbing the last 2 characters (assuming there is no 1 to the left of the
decimal point).
Thanks,
Marty
Marty Backe
-------------------------------
mgb...@usa.net
http://www.lucidway.org
>What's the conventional Tcl way of performing the equivalent of "expr int(.29 *
>100)" such that the correct value (28) is returned?
% expr double(.28) * 100
28.0
--
David Gravereaux <davy...@pobox.com>
Tomasoft Engineering, Hayward, CA
[species: human; planet: earth,milkyway,alpha sector]
Please be aware of the 7.5 year ping times when placing a call from alpha centari
Uh, I suppose you meant so that 29 is returned? As you noted,
28 is returned due to the idiosyncracies of floating point math,
exposed at the C level.
I suppose the best way would be what you guessed, and what many
financial apps do, which is to do everything with integers and
place the . where it's supposed to go.
--
Jeff Hobbs The Tcl Guy
Senior Developer http://www.ActiveState.com/
Tcl Support and Productivity Solutions
> What's the conventional Tcl way of performing the equivalent of "expr int(.29 *
> 100)" such that the correct value (28) is returned?
expr round(.29 * 100)
int(.29*100+.5)
Typical Fortran solution.
--
Thor--Stockholm--Sverige
expr int([expr {.29*100}])
Which to me, who am not a mathematician, is a surprise.
That is, I am surprised that it does NOT yield the same
result as:
expr int(.29*100)
or:
expr int((.29*100))
Which to me should be equivalent to
set foo [ expr .29 * 100 ]
expr int($foo)
Because of what to me seem to be grouping operators which
I guess are not...
Keep in mind that Tcl was invented by engineers, not mathematicians. ;-)
> That is, I am surprised that it does NOT yield the same
> result as:
>
> expr int(.29*100)
>
> or:
>
> expr int((.29*100))
After evaluation of [set ::tcl_precision 17], all of these variations
will agree that the full-precision answer is 28.
With the default value of $::tcl_precision, shimmering to and from
a string representation loses precision, which can provide rounding
that has more intuitive appeal.
Best answer to all of this: if you need precise operations on exactly
2 decimal places, do your work with integers and multiply/divide
by 100 on I/O.
--
| Don Porter Mathematical and Computational Sciences Division |
| donald...@nist.gov Information Technology Laboratory |
| http://math.nist.gov/~DPorter/ NIST |
|______________________________________________________________________|
I grovel at your keyboard feet...
Marty
Can someone explain why int(.29 * 100) should be 28 instead of 29 ? I'm
confused.
--
--
"I know of vanishingly few people ... who choose to use ksh." "I'm a minority!"
<URL: mailto:lvi...@cas.org> <URL: http://www.purl.org/NET/lvirden/>
Even if explicitly stated to the contrary, nothing in this posting
% set tcl_precision 17
17
% expr .29
0.28999999999999998
So I guess really we should use
% expr int([expr 0.29 * 100])
29
Doesn't make much sense to me though. As
% expr 0.29
0.29
and
%expr int([expr [expr 0.29] * [expr 100]])
29
but
% expr int(0.29 * 100)
28
so where does this set tcl_precision 17 come into it?
Gordon
Gordon Johnstone <gor...@thornsds.co.uk> wrote:
> So I guess really we should use
> % expr int([expr 0.29 * 100])
> 29
No. Depending on Tcl's particular shimmering behavior *and* the
value of $::tcl_precision is a terribly fragile thing to do.
The right answer -- compute with integers when you demand exact
calculations -- has been given three times in this thread.
Here are a few Wiki references as well:
http://mini.net/tcl/879.html
http://mini.net/tcl/1296.html
It wasn't until I figured out that as soon as Tcl has to do math with
the 0.29, it changes the string "0.29" to the nearest equivalent binary
representation, which is less than 0.29, that I figured out where the
problem was. I forgot that Tcl's default math is less precise.
Maybe I'm being thick here, but I still can't see why
% expr 0.29 * 100
29.0
and
% expr int([expr 0.29 * 100])
29
but
% expr int(0.29 * 100)
28
so why does the nearest equivalent binary representation change?
Gordon
The binary representation of 0.29 (as shown in an earlier reply)
is actually slightly LESS than 0.29: 0.2899999928 (or something
very close). Multiplying by 100 may actually result, DEPENDING
ON THE PRECISE DETAILS, in 28.9999999967 or 29.00000001 or whatever.
So, just truncating it as in "int(0.29*100)" may result in 28 in
the first case and 29 in the second.
As stated before, use the round() function instead of int(). This
rounds to the nearest integer, rather than brutally cut off
the fractional part.
Regards,
Arjen Markus
The [expr] command converts "0.29" into a double precision number, and
"100" into an integer. It has to promote both to doubles to perform the
multiplication, so the result is a double precision number. The actual
number, due to roundoff, is something like 28.999999999999996, but for
display purposes, it is rounded to 29.0.
> and
>
> % expr int([expr 0.29 * 100])
> 29
I suspect, in this case, that the result of the inner [expr] is getting
converted into a string. Then the outer [expr] command just takes the
integer value of "29.0". You can change the behavior of this expression
by setting tcl_precision to 17.
> % expr int(0.29 * 100)
> 28
In this case, the computed value is 28.999999999999996, and the int()
function is supposed to round it down to 28.
Bob
--
Bob Techentin techenti...@mayo.edu
Mayo Foundation (507) 538-5495
200 First St. SW FAX (507) 284-9171
Rochester MN, 55901 USA http://www.mayo.edu/sppdg/
(Note: my newsserver doesn't have the initial posting, so I've put this
into the posting I have in this thread)
There are other things that apparently have to be repeated every once in a
while (I didn't find something like this in the wiki):
Never ever at all use floating point (FP) math as increment, this may
have desastrous effects! This affects _all_ languages that use FP math,
the examples given here are in Tcl but are also valable for C, perl,
python, Pascal, Basic, ... whatever you can think of. Depending of the
internal representation of your FP values (float, double, others), you
mileage may vary, but the effects will always lurk somewhere in the
background and strike when you expect it the least (preferably after your
first real roll-out of your software :)
1st example: This loop runs forever although the logic dictates that it
should run exactly 3 times
for {set x 0.0} {$x != 0.3} {set x [expr {$x+0.1}]} {
puts $x
}
If you really have to use a FP loop variable, use the < and > comparators
instead of != as shown in example 1b:
for {set x 0.0} {$x < .3} {set x [expr {$x+0.1}]} {
puts $x
}
This will run three times, as expected ... BUT! ... sooner or later,
you'll also run into big trouble with this. Look at the next example:
set count 0
for {set x 0.0} {$x < .3} {set x [expr {$x+0.0001}]} {
puts $x
incr count
}
puts "x is $x"
puts "count is $count"
This one should run 3000 times, right? Well, it doesn't, it runs 3001
times as the count variable will tell you (try this loop with 0.001,
0.0001, 0.00001 and 0.000001 as increment ... especially the last gets
interesting at the end).
******* The effects of imprecise FP math is cumulative! *********
The recommended way to write loops that use or compute FP math variables
is something like this:
for {set count 0} {$count < 3000} {incr count} {
set x [expr {$count * 0.0001}]
puts $x
}
puts "x is $x"
puts "count is $count"
This one will run the expected 3000 times and the computed x values will
not contain cumulative FP math errors.
***** Use an integer as counter and also derive computed FP values from
this integer! *****
Salut,
Bastien
--
Bastien Chevreux -- Life Science Information Manager
MWG Biotech AG -- Anzinger Strasse 7a -- D-85560 Ebersberg; Germany
Phone: +49 8092 8289 309 -- Fax: +49 8092 8289 310
perhaps the problem is not string-conversions, but users who do not
curly-brace expr's argument ...
if someone wants 28.7 to be floor()ed to 28, but 28.999999998 to be
recognized as 29, then here is the answer:
set myfloat 28.99999999998
puts [expr {int($myfloat+$::eps)}]
29
where global eps is set at program start to 0.0000000001 or a value
somewhere in the order of the expected deviations from the exact avlues.
If you expect the original number to have no more than two
digits after the decimal point, then even an ::eps of 0.001 could
be ok, because 28.99+0.001 would still be floor()ed
--
Newsflash: Sproingy made it to the ground !
read more ... <http://avl.enemy.org/sproingy>
Gordon Johnstone wrote:
> % expr 0.29 * 100
> 29.0
OK, here $tcl_precision has the default value, so what happens is:
(1) The three string arguments, 0.29, *, and 100 are concatenated
(2) The resulting expression is parsed and evaluated, giving
28.999999999999996
(3) The result is converted to a string at default precision,
which rounds it to 29.0
> % expr int([expr 0.29 * 100])
> 29
The first three steps are the same as above; the result of the
[expr] command is converted to the string "29.0". This string
is interpolated into the args, giving the outer [expr] an
argument of "int(29.0)" -- which is, of course, 29. The key point
here is that the arguments to the outer [expr] were not in braces,
so the result of the inner [expr] was substituted as a string,
not a double.
> % expr int(0.29 * 100)
> 28
Here, now, the [expr] command is presented with three string args,
"int(0.29", "*", and "100)". These are concatenated to give
"int(0.29*100)". The parser now computes 0.29 * 100, which
is really 0.28999999999999998 * 100.0; the result is
28.999999999999996. The greatest integer in this result is 28.
The moral of the story is that [expr]'s behavior will unexpectedly
convert intermediate args to strings (with loss of precision)
unless the expression is enclosed in braces. (Enclosing the
expression in braces also allows bytecode compilation, so it's
MUCH faster as well!)
--
73 de ke9tv/2, Kevin KENNY GE Corporate R&D, Niskayuna, New York, USA
> That is the heart of the matter, that the outer 'expr' is being given
> the string "29.0", and it really points out the historical roots of
> Tcl as a single-type-which-is-string language. What is scary is that
> the core's internal Tcl object representation could, I suppose, at
> some point be coded in a way that handles this situation, and it would
> "invisibly" change the behavior! Indeed you might argue from the
> expr man page that this situation should already be handled:
> "Expressions almost always yield numeric results (integer
> or floating-point values)"
> Don't get me wrong, I love Tcl, but as it grew and abandoned its
> inefficient single-type-which-is-string roots, the _real_
> programming language design issues started coming out and there's alot
> of issues that get raised that make "slapping on" efficient object
> representation or bytecode compiling much deeper and harder issues
> than imagined.
> Oh for Tcl's usability and ML's rigor!!
> Jon.
Hi, take it easy:
#include<stdio.h>
int main()
{
printf("%lf =? %d\n", 0.29 * 100, (int) (0.29*100));
return 0;
}
I see as result:
29.000000 =? 28
Alexei
: What's the conventional Tcl way of performing the equivalent of "expr
: int(.29 * : 100)" such that the correct value (28) is returned?
just add 0.5 after multiplying
expr int(.29*100+0.5)
It is traditional way to round integers rather than truncating them.
Other way around is to use rounding capabilities embedded into
C printf (and consequentually tcl format) function
format %.0f [expr 0.29*100]
--
Odd that we think definitions are definitive. :-)
-- Larry Wall in <1997022219...@wall.org>