[issue41598] rnd() + rndup() in math

2020-08-22 Thread Vedran Čačić

Vedran Čačić  added the comment:

> use more digits to manage rounding in decimal base, not only one but more (i 
> should think better and experiment on how many)

You don't have to. It's infinitely many. :-P Think, how many decimal digits 
would you need to accurately round numbers to a closest third (one trinary 
digit)? Here are some decimal digits: 2.1. If the next digit is 5, then 
it rounds to 2.0. If it is 7, it rounds to 2.1 (base 3). If it is 6, you still 
don't know anything. It can go arbitrarily far. Of course, the probability is 
lower with every digit, and at some point it becomes acceptable (you said for 
yourself it's acceptable even with one extra digit), but it's not 
mathematically correct.

And that one bit was just an illustration. In real life, 64-bit machines 
usually use at least 80-bit precision, so 16 extra bits. But it doesn't help 
your case, for the above reasons: this is simply not decimal rounding.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue41598] rnd() + rndup() in math

2020-08-22 Thread marco_ocram


marco_ocram  added the comment:

i think in your long post you have underlined among others:
1. in binary implementation of floats one bit was reserved to rounding;
2. this one bit is not enough to manage a correct rounding in a converted 
decimal base;
my considerations:
3. someone i think had evaluated deciding rounding didn't worth more 
significative digits;
4. the only solution in general can be use more digits to manage rounding in 
decimal base, not only one but more (i should think better and experiment on 
how many);
5. the problem in general cannot be solved by an implementation of rounding but 
from a revision of the implementation of floats (i have solved pretty well my 
personal need but i'm speaking in general);
6. this is not really a weak impact i think, despite it's not impossible;
7. and last, i think the current implementation could derive from historical 
reasons when we worked on 8 bits machines, not 64 bits, and one bit was of 
worth, but i may be wrong.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue41598] rnd() + rndup() in math

2020-08-22 Thread Vedran Čačić

Vedran Čačić  added the comment:

I think you don't know what paraphrasing means. Or maybe I don't know it, but 
in any case, I'm well aware what Von Neumann said, and how it is usually 
misunderstood in today's society. "Living in the state of sin" is simply an 
emotionally powerful metaphor, and it pertains to the philosophical 
impossibility of something, whether or not there are strong reasons to "do it 
anyway" and even good methods of faking/approximating it. Same as "detecting 
infinite loops programmatically", "passing objects by value", or "having side 
effects in a referentially transparent language". :-)

I agree that Python is a language where practicality beats purity, and as I 
said, there is a sensible interface for the implementation about which we agree 
(except for a detail: I'd actually _force_ people to import the rounding modes 
from decimal module, to at least hint to them what exactly they are doing and 
what the preferred approach is). But I still think that arguments for that are 
very weak.

First, yes, there is a very small probability of calculations _randomly_ ending 
with abc.de5...0 (in the Platonic sense) [so it actually manifests as a 
problem], but there is much greater probability (though still small) that 
calculations _theoretically_ ending with abc.de5 actually end with 
abc.de4...7 [and thus are rounded down anyway, despite your insistence on 
modes]. People _will_ think "hey, I did the right thing by specifying the 
mode--why does it still acts funny?"

"there are reasonable use-cases for different rounding modes even when using 
floats.": absolutely. But they are _binary_ rounding modes! I think you should 
read the standard. It is actually two standards, one for binary and one for 
decimal arithmetic. (The second one is implemented fairly faithfully in the 
decimal module; the first one is, through libc, the one that Python floats are 
based upon).

The [binary] standard says, more or less, that the result should be as if the 
value was calculated with the infinite number or extra [binary] digits (of 
course, in practice it should just be some extra precision, but again, of 
_binary_ digits), and then, when fitting that value into a smaller [binary] 
register with smaller available number of [binary] digis, all extra [binary] 
digits are discarded (not stored) and some ending [binary] digits retained are 
modified according to the value of some of the discarded [binary] digits and 
the rounding mode. For example:

"""
The 24-bit significand will stop at position 23, shown as the underlined bit 0 
above. The next bit, at position 24, is called the round bit or rounding bit. 
It is used to round the 33-bit approximation to the nearest 24-bit number 
(there are specific rules for halfway values, which is not the case here). This 
bit, which is 1 in this example, is added to the integer formed by the leftmost 
24 bits.
"""

You see it speaks about _bits_, not decimal digits. In the same way, _decimal_ 
rounding would take a value calculated with some extra precision of _decimal_ 
digits, and when storing them with the smaller number of _decimal_ digits, 
discard extra... blah blah as above. But you cannot round binary numbers to 
decimal digits. It's as if you tried to round 0.45 to one trinary digit after 
the integer point. The answer is 0.1 in base 3, but it isn't expressible in 
decimals. And it doesn't work:

>>> Decimal('0.45').quantize(Decimal(1/3))
decimal.InvalidOperation: []

because the authors of quantize have thought about that. The documentation says 
"""Unlike other operations, if the length of the coefficient after the quantize 
operation would be greater than precision, then an InvalidOperation is 
signaled.""" In effect, rounding cannot increase the number of significant 
digits. And it would do that if you're rounding to an incompatible base, 
whether 2 to 10 or 10 to 3.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue41598] rnd() + rndup() in math

2020-08-22 Thread marco_ocram


marco_ocram  added the comment:

@Vedran:
I'm sorry about my "quick-and-dirty implementations". As i've already stated 
the problem's more deep i expect, despite the second half up rounding for my 
needs now (after your observation) work well. I've verified other languages 
have the same problem with floats operations and roundings.

@Steven:
"Are you satisfied that adding a rounding mode to the built-in `round` function 
is a better solution than a series of functions in the math module? If so, I 
will change the title to reflect that."
I fully agree with the sentence and with all the content of your writings. I 
think the decimal module is excellent and can do an extraordinary work (as 
extraordinary i suppose was the work of its coders) but floats also are fine 
for common people use, i see only the rounding as main them problem. It's very 
unpleasant to round 2.8-1.3 half up and without tricks obtain a misleading 
results.
I think working on the last decimal digit if all are used the problem could be 
solved, but with a lot of study (at least for me if i have) cause one purpose 
have to be maintain good performances in addition to results always corrects.

This is only a possibility to improve the core language, i think one function 
with more common rounding ways, as in wikipedia (in gnu c i don't see just "to 
nearest, ties away from zero" or half up we discuss) or in decimals module, can 
be useful to reduce the need of individual coders implementation on a not so 
simple question cause them needs not satisfied by the round() as banking 
rounding.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue41598] rnd() + rndup() in math

2020-08-21 Thread Steven D'Aprano


Steven D'Aprano  added the comment:

Marco, it is better to give a description of the functionality required rather 
than a simplistic and incorrect implementation :-)

(Not that I am likely to provide a better implementation without a lot of 
study.)

Regardless of whether you or I agree with the decision to move to Banker's 
Rounding, that was done about a decade ago, and it matches decisions made by 
other languages such as Julia. Changing the default will break people's code 
and we do not do that lightly, or at all, without a very good reason.

As Tim Peters describes here:

https://mail.python.org/pipermail/python-dev/2008-January/075873.html

many older languages implemented rounding by "add half and chop", more because 
it was cheap than for its mathematical properties.

Are you satisfied that adding a rounding mode to the built-in `round` function 
is a better solution than a series of functions in the math module? If so, I 
will change the title to reflect that.

Vedran: I don't think the availability of different rounding modes will have 
any effect at all on whether or not people believe floats are real decimal 
numbers rather than binary floats. People already believe that. I think we are 
better off acknowledging that there are reasonable use-cases for different 
rounding modes even when using floats.

According to this:

https://www.gnu.org/software/libc/manual/html_node/Rounding.html

IEEE-754 only requires four rounding modes, defaulting to the same Banker's 
Rounding that Python uses. Wikipedia says there are five:

https://en.wikipedia.org/wiki/IEEE_754#Rounding_rules


Regardless of whether there are four or five, I see no technical reason why we 
couldn't offer the full set of eight used by the decimal module.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue41598] rnd() + rndup() in math

2020-08-21 Thread Steven D'Aprano


Steven D'Aprano  added the comment:

Vedran: you are quoting von Neumann out of context, he was talking about 
generating random numbers, not rounding, and in the seven decades since he made 
his famous witticism, we of course know that there is absolutely nothing wrong 
with generating random numbers via arithmetical methods so long as you do it 
correctly :-)

I have read your comments in the linked Stackoverflow answer, and I don't agree 
that we shouldn't be rounding binary floats. I do, however, agree with your 
comment here that we ought to add a rounding mode to the built-in round 
function, that would be a much better idea than adding a multitude of 
individual rounding functions.

I proposed this idea some time ago:

https://mail.python.org/archives/list/python-...@python.org/message/DVS3XSAKW37NDD37BE3IOCKRBRV3Y5A6/

--
nosy: +steven.daprano

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue41598] rnd() + rndup() in math

2020-08-21 Thread Vedran Čačić

Vedran Čačić  added the comment:

Yes, and these functions are completely fine for your personal library, if you 
need such things. But they really have no place in math module, since (1) they 
aren't always correct, (2) it's incredibly difficult to characterize exactly 
when they are, and (3) even when they are correct, they just give people wrong 
impression about floats somehow being decimal numbers.

If you need to use floats, reconcile yourself with the idea that they don't 
represent all decimal numbers, not even the ones with just one decimal place, 
and any algorithm dealing with decimal digits will be just an approximation.

If you want the best approximation the humanity can muster, backed with 
empirical data and theoretical considerations, use the round() builtin. 

If you want to just have something that works according to your preconceived 
notions in some cases, while failing mysteriously in others, use your 
quick-and-dirty implementations.

If you want exact decimal results, use the decimal module, and specify the 
rounding mode as you wish.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue41598] rnd() + rndup() in math

2020-08-21 Thread marco_ocram


marco_ocram  added the comment:

rndup is not correct and rnd work smooth only for numbers about < e16 until 
they have a fractional part. it's interesting but not simple.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue41598] rnd() + rndup() in math

2020-08-21 Thread marco_ocram


marco_ocram  added the comment:

thank you very much about the hints, i've improved the code as follows.

def rnd(x, n=0):
   a = x*10**(n + 1)
   b = int(a)
   if abs(a - b) >= 0.5: b += 1 if x >= 0 else -1
   a = b/10
   b = int(a)
   if abs(a - b) >= 0.5: b += 1 if x >= 0 else -1
   return b/10**n

def rndup(x, n=0):
   a = x*10**(n + 1)
   b = int(a)
   if abs(a - b) > 0: b += 1 if x >= 0 else -1
   a = b/10
   b = int(a)
   if abs(a - b) > 0: b += 1 if x >= 0 else -1
   return b/10**n

now it manage well your cases ...

print(rnd(2.8 - 1.3), 2.8 - 1.3)
print(rnd(1.8 - 1.3), 1.8 - 1.3)
print(rnd(-2.8 + 1.3), -2.8 + 1.3)
print(rnd(-1.8 + 1.3), -1.8 + 1.3)

results ...

2.0 1.4998
1.0 0.5
-2.0 -1.4998
-1.0 -0.5

we have to define limits caused by the floating point internal representation 
but for general use in my opinion can work fine. do you see other cases where 
it cracks?

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue41598] rnd() + rndup() in math

2020-08-20 Thread Vedran Čačić

Vedran Čačić  added the comment:

Yes, we can do better than ms (whatever that means). And we do exactly that in 
the `decimal` module. Floats are not the right target for your algorithms, 
because they don't have decimal digits. It's as if you're trying to round words 
in English language. :-D (In fact, with words you'd probably have better luck, 
since they _can_ represent decimal numbers exactly.)

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue41598] rnd() + rndup() in math

2020-08-20 Thread marco_ocram


marco_ocram  added the comment:

print(a := rnd(rnd(2.8-1.3, 15)))
print(b := rnd(rnd(1.8-1.3, 15)))
print(a == b)

results ...

2.0
1.0
False

it's the last significative digit that have problems, we could work on the code 
to correct this and experiment if someone is interested. by the way the last 
ultra successful excel 365 - i've just tried - have the same problem. we can do 
better then ms?

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue41598] rnd() + rndup() in math

2020-08-20 Thread marco_ocram


marco_ocram  added the comment:

i want to be more clear, these could be useful.

https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/round-function
This VBA function returns something commonly referred to as bankers rounding. 
So be careful before using this function. For more predictable results, use 
Worksheet Round functions in Excel VBA.

https://docs.microsoft.com/en-us/dotnet/api/system.math.round?view=netcore-3.1
Rounding to nearest, or banker's rounding
Midpoint values are rounded to the nearest even number. For example, both 3.75 
and 3.85 round to 3.8, and both -3.75 and -3.85 round to -3.8. This form of 
rounding is represented by the MidpointRounding.ToEven enumeration member.
Rounding to nearest is the standard form of rounding used in financial and 
statistical operations. It conforms to IEEE Standard 754, section 4. When used 
in multiple rounding operations, it reduces the rounding error that is caused 
by consistently rounding midpoint values in a single direction. In some cases, 
this rounding error can be significant.

this is how work the current round() function, but if i don't need to do 
successive rounding but only adjust data i prefer...

https://docs.microsoft.com/en-us/dotnet/api/system.math.round?view=netcore-3.1
Rounding away from zero
Midpoint values are rounded to the next number away from zero. For example, 
3.75 rounds to 3.8, 3.85 rounds to 3.9, -3.75 rounds to -3.8, and -3.85 rounds 
to -3.9. This form of rounding is represented by the 
MidpointRounding.AwayFromZero enumeration member.
Rounding away from zero is the most widely known form of rounding.

i can understand in decimal the default is banking or accounting rounding and i 
can adjust the context, but in normal calculations i think will be more usefule 
the common rounding, or also the possibility to choose with an argument. i have 
simply implemented my functions, this is only for the sake of improving the 
language. cheers.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue41598] rnd() + rndup() in math

2020-08-20 Thread Vedran Čačić

Vedran Čačić  added the comment:

ROUND_HALF_UP is not chosen because it's popular, but because it's the best way 
to compensate for errors in representing decimal numbers by binary numbers.

Thinking that floats are decimal numbers is going to bite you anyway sooner or 
later. For example, would you expect (with your implementation) rnd(2.8-1.3) == 
rnd(1.8-1.3) ?

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue41598] rnd() + rndup() in math

2020-08-20 Thread marco_ocram


marco_ocram  added the comment:

revision of "if you need to realize decimal.ROUND_DOWN function trunc is 
already fine." --> "if you need to realize decimal.ROUND_DOWN function int() or 
math.trunc() are already fine."

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue41598] rnd() + rndup() in math

2020-08-20 Thread marco_ocram


marco_ocram  added the comment:

about floats ...

https://docs.python.org/3/library/functions.html#round
"if two multiples are equally close, rounding is done toward the even choice 
(so, for example, both round(0.5) and round(-0.5) are 0, and round(1.5) is 2)"

i'm sure lots of users don't need this but a common math rounding as ...

https://docs.python.org/3/library/decimal.html#rounding-modes
decimal.ROUND_HALF_UP and as option in some cases decimal.ROUND_UP

the proposed function realize just this, for either positive and negative vals.

if you need to realize decimal.ROUND_DOWN function trunc is already fine.

i'm as mathematician have never used other roundings, i know the current round 
function work well in the accounting fiels but only there. in other languages 
sometimes the same problem (for example ms ones as old vb), really i and others 
don't know the reason.

about the revision of the standard function to accept an additional argument 
for different rounding types, for example all available for decimals, is ok for 
me, better if the default will be the normal accepted math rounding 
decimal.ROUND_HALF_UP ...

https://docs.python.org/3/library/decimal.html#decimal.BasicContext
decimal.BasicContext
This is a standard context defined by the General Decimal Arithmetic 
Specification. Precision is set to nine. Rounding is set to ROUND_HALF_UP.

about the floating point approximation in my opinion if you aren't a scientist 
you can find it pretty nice if you apply the right roundings. i think to have 
documented enough my request. take care.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue41598] rnd() + rndup() in math

2020-08-20 Thread Vedran Čačić

Vedran Čačić  added the comment:

If we want to proceed with this, much better way would be to add a rounding 
mode optional argument to `round` builtin.

But really, anyone trying to precisely round a decimal representation of a 
binary floating point number is, to paraphrase von Neumann, living in the state 
of sin. See https://stackoverflow.com/a/51146310/1875565.

--
nosy: +veky

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue41598] rnd() + rndup() in math

2020-08-20 Thread Karthikeyan Singaravelan


Change by Karthikeyan Singaravelan :


--
nosy: +mark.dickinson, rhettinger, stutzbach

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue41598] rnd() + rndup() in math

2020-08-20 Thread Ronald Oussoren


Ronald Oussoren  added the comment:

Adding new functions that are extreme similar to existing functions requires a 
better rationale than your giving.

In particular, both are similar to the builtin function "round" 
(https://docs.python.org/3/library/functions.html#round>).

--
nosy: +ronaldoussoren

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue41598] rnd() + rndup() in math

2020-08-20 Thread marco_ocram


New submission from marco_ocram :

hello, please insert following simple but useful roundings in new lib math.

def rnd(x, n=0):
   a = x*10**n
   b = int(a)
   if abs(a - b) >= 0.5: b += 1 if x >= 0 else -1
   return b/10**n

def rndup(x, n=0):
   a = x*10**n
   b = int(a)
   if abs(a - b) > 0: b += 1 if x >= 0 else -1
   return b/10**n

thanks a lot, marco.

--
components: Library (Lib)
messages: 375695
nosy: marco_ocram
priority: normal
severity: normal
status: open
title: rnd() + rndup() in math
type: enhancement
versions: Python 3.9

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com