Hi I'm wondering if we do not have a bug here 12.222222222222221 roundTo: 0.1 "12.200000000000001" 12.222222222222221 roundTo: 0.01 "12.22" Stef
I think that's called 'welcome to the wonderfull world of floating points approximations' and not a bug :( See unums/Gustafson for something more interesting on the subject. Thierry 2016-10-23 18:16 GMT+02:00 stepharo <stepharo@free.fr>:
Hi
I'm wondering if we do not have a bug here
12.222222222222221 roundTo: 0.1 "12.200000000000001"
12.222222222222221 roundTo: 0.01 "12.22"
Stef
I think lisp solves this problem by using integers for its floats , or something along those line, I remember reading about it somewhere when I was studying common lisp 5 years ago Or maybe my memory fails me On Sun, Oct 23, 2016 at 7:24 PM Thierry Goubier <thierry.goubier@gmail.com> wrote:
I think that's called 'welcome to the wonderfull world of floating points approximations' and not a bug :(
See unums/Gustafson for something more interesting on the subject.
Thierry
2016-10-23 18:16 GMT+02:00 stepharo <stepharo@free.fr>:
Hi
I'm wondering if we do not have a bug here
12.222222222222221 roundTo: 0.1 "12.200000000000001"
12.222222222222221 roundTo: 0.01 "12.22"
Stef
aaaaaand I was wrong, my bad On Sun, Oct 23, 2016 at 7:29 PM Dimitris Chloupis <kilon.alios@gmail.com> wrote:
I think lisp solves this problem by using integers for its floats , or something along those line, I remember reading about it somewhere when I was studying common lisp 5 years ago
Or maybe my memory fails me
On Sun, Oct 23, 2016 at 7:24 PM Thierry Goubier <thierry.goubier@gmail.com> wrote:
I think that's called 'welcome to the wonderfull world of floating points approximations' and not a bug :(
See unums/Gustafson for something more interesting on the subject.
Thierry
2016-10-23 18:16 GMT+02:00 stepharo <stepharo@free.fr>:
Hi
I'm wondering if we do not have a bug here
12.222222222222221 roundTo: 0.1 "12.200000000000001"
12.222222222222221 roundTo: 0.01 "12.22"
Stef
I see. I writing a little example for my new newbies book now how can get only two digits after the . Stef Le 23/10/16 à 18:23, Thierry Goubier a écrit :
I think that's called 'welcome to the wonderfull world of floating points approximations' and not a bug :(
See unums/Gustafson for something more interesting on the subject.
Thierry
2016-10-23 18:16 GMT+02:00 stepharo <stepharo@free.fr <mailto:stepharo@free.fr>>:
Hi
I'm wondering if we do not have a bug here
12.222222222222221 roundTo: 0.1 "12.200000000000001"
12.222222222222221 roundTo: 0.01 "12.22"
Stef
3.1479 roundUpTo: 0.01 is what I was looking for. Stef Le 23/10/16 à 18:35, stepharo a écrit :
I see.
I writing a little example for my new newbies book
now how can get only two digits after the .
Stef
Le 23/10/16 à 18:23, Thierry Goubier a écrit :
I think that's called 'welcome to the wonderfull world of floating points approximations' and not a bug :(
See unums/Gustafson for something more interesting on the subject.
Thierry
2016-10-23 18:16 GMT+02:00 stepharo <stepharo@free.fr <mailto:stepharo@free.fr>>:
Hi
I'm wondering if we do not have a bug here
12.222222222222221 roundTo: 0.1 "12.200000000000001"
12.222222222222221 roundTo: 0.01 "12.22"
Stef
I usually use 12.222222222222221 round: 2 "12.22". Peter On Sun, Oct 23, 2016 at 06:38:47PM +0200, stepharo wrote:
3.1479 roundUpTo: 0.01 is what I was looking for.
Stef
Le 23/10/16 à 18:35, stepharo a écrit :
I see.
I writing a little example for my new newbies book
now how can get only two digits after the .
Stef
Le 23/10/16 à 18:23, Thierry Goubier a écrit :
I think that's called 'welcome to the wonderfull world of floating points approximations' and not a bug :(
See unums/Gustafson for something more interesting on the subject.
Thierry
2016-10-23 18:16 GMT+02:00 stepharo <stepharo@free.fr <mailto:stepharo@free.fr>>:
Hi
I'm wondering if we do not have a bug here
12.222222222222221 roundTo: 0.1 "12.200000000000001"
12.222222222222221 roundTo: 0.01 "12.22"
Stef
Thanks because roundUpTo: did not make it either :) Le 23/10/16 à 18:45, Peter Uhnak a écrit :
I usually use 12.222222222222221 round: 2 "12.22".
Peter
On Sun, Oct 23, 2016 at 06:38:47PM +0200, stepharo wrote:
3.1479 roundUpTo: 0.01 is what I was looking for.
Stef
Le 23/10/16 à 18:35, stepharo a écrit :
I see.
I writing a little example for my new newbies book
now how can get only two digits after the .
Stef
Le 23/10/16 à 18:23, Thierry Goubier a écrit :
I think that's called 'welcome to the wonderfull world of floating points approximations' and not a bug :(
See unums/Gustafson for something more interesting on the subject.
Thierry
2016-10-23 18:16 GMT+02:00 stepharo <stepharo@free.fr <mailto:stepharo@free.fr>>:
Hi
I'm wondering if we do not have a bug here
12.222222222222221 roundTo: 0.1 "12.200000000000001"
12.222222222222221 roundTo: 0.01 "12.22"
Stef
I use the Printf package for that. v := 65.456. 'With 2 decimal digits: %5.2f, or 3 like this: %6.3f' printf: {v. v}. With 2 decimal digits: 65.45, or 3 like this: 65.456 It is in http://www.smalltalkhub.com/#!/~philippeback/HOExtras Printf I am just used to C printf and well, I like the way it works. Phil On Sun, Oct 23, 2016 at 6:35 PM, stepharo <stepharo@free.fr> wrote:
I see.
I writing a little example for my new newbies book
now how can get only two digits after the .
Stef
Le 23/10/16 à 18:23, Thierry Goubier a écrit :
I think that's called 'welcome to the wonderfull world of floating points approximations' and not a bug :(
See unums/Gustafson for something more interesting on the subject.
Thierry
2016-10-23 18:16 GMT+02:00 stepharo <stepharo@free.fr>:
Hi
I'm wondering if we do not have a bug here
12.222222222222221 roundTo: 0.1 "12.200000000000001"
12.222222222222221 roundTo: 0.01 "12.22"
Stef
+1. Unless you're dealing with fixed precision* entities, it's usually better to specify digits to display in printing methods themselves (#printShowingDecimalPlaces: & friends in base image). As per previous discussions around this that arise every second year or so, rounding the number itself (as long as we're dealing with floats) will never work as you want reliably. Cheers, Henry * And in that case, you'd use ScaledDecimals
On 23 Oct 2016, at 7:08 , phil@highoctane.be wrote:
I use the Printf package for that.
v := 65.456. 'With 2 decimal digits: %5.2f, or 3 like this: %6.3f' printf: {v. v}.
With 2 decimal digits: 65.45, or 3 like this: 65.456
It is in http://www.smalltalkhub.com/#!/~philippeback/HOExtras <http://www.smalltalkhub.com/#!/~philippeback/HOExtras>
Printf
I am just used to C printf and well, I like the way it works.
Phil
On 25 Oct 2016, at 11:05, Henrik Johansen <henrik.s.johansen@veloxit.no> wrote:
+1.
Unless you're dealing with fixed precision* entities, it's usually better to specify digits to display in printing methods themselves (#printShowingDecimalPlaces: & friends in base image). As per previous discussions around this that arise every second year or so, rounding the number itself (as long as we're dealing with floats) will never work as you want reliably.
Cheers, Henry
* And in that case, you'd use ScaledDecimals
YES !! So true.
On 23 Oct 2016, at 7:08 , phil@highoctane.be wrote:
I use the Printf package for that.
v := 65.456. 'With 2 decimal digits: %5.2f, or 3 like this: %6.3f' printf: {v. v}.
With 2 decimal digits: 65.45, or 3 like this: 65.456
It is in http://www.smalltalkhub.com/#!/~philippeback/HOExtras
Printf
I am just used to C printf and well, I like the way it works.
Phil
Ok for the advice Now round: aNumberOfDigits seems to work fine. Stef Le 25/10/16 à 11:05, Henrik Johansen a écrit :
+1.
Unless you're dealing with fixed precision* entities, it's usually better to specify digits to display in printing methods themselves (#printShowingDecimalPlaces: & friends in base image). As per previous discussions around this that arise every second year or so, rounding the number itself (as long as we're dealing with floats) will never work as you want reliably.
Cheers, Henry
* And in that case, you'd use ScaledDecimals
On 23 Oct 2016, at 7:08 , phil@highoctane.be <mailto:phil@highoctane.be> wrote:
I use the Printf package for that.
v := 65.456. 'With 2 decimal digits: %5.2f, or 3 like this: %6.3f' printf: {v. v}.
With 2 decimal digits: 65.45, or 3 like this: 65.456
It is in http://www.smalltalkhub.com/#!/~philippeback/HOExtras <http://www.smalltalkhub.com/#%21/%7Ephilippeback/HOExtras>
Printf
I am just used to C printf and well, I like the way it works.
Phil
2016-10-25 23:22 GMT+02:00 stepharo <stepharo@free.fr>:
Ok for the advice
Now round: aNumberOfDigits seems to work fine.
Stef
No it's not, but issues are timing out too fast ;) https://pharo.fogbugz.com/f/cases/15471/Can-t-round-Float-fmax-to-2-decimal-... https://pharo.fogbugz.com/f/cases/15472/Fraction-rounding-to-n-decimal-place... https://pharo.fogbugz.com/f/cases/15473/Float-round-to-n-decimal-places-is-n... I hate to say that, but try in python, they got it right...
Le 25/10/16 à 11:05, Henrik Johansen a écrit :
+1.
Unless you're dealing with fixed precision* entities, it's usually better to specify digits to display in printing methods themselves (#printShowingDecimalPlaces: & friends in base image). As per previous discussions around this that arise every second year or so, rounding the number itself (as long as we're dealing with floats) will never work as you want reliably.
Cheers, Henry
* And in that case, you'd use ScaledDecimals
On 23 Oct 2016, at 7:08 , phil@highoctane.be wrote:
I use the Printf package for that.
v := 65.456. 'With 2 decimal digits: %5.2f, or 3 like this: %6.3f' printf: {v. v}.
With 2 decimal digits: 65.45, or 3 like this: 65.456
It is in http://www.smalltalkhub.com/#!/~philippeback/HOExtras
Printf
I am just used to C printf and well, I like the way it works.
Phil
Hi nicolas So what is the solution? We can integrate fast a solution. I would really like to see them fix in Pharo 60. I'm writing a book for newbie and this is the third time I change one chapter so may be I should stop and throw away this chapter. You see I'm not good in math so even if I try hard I will not have a good solution. This is the same for marcus and most people in our team. I reopened these issues. Stef
2016-10-25 23:22 GMT+02:00 stepharo <stepharo@free.fr <mailto:stepharo@free.fr>>:
Ok for the advice
Now round: aNumberOfDigits seems to work fine.
Stef
No it's not, but issues are timing out too fast ;) https://pharo.fogbugz.com/f/cases/15471/Can-t-round-Float-fmax-to-2-decimal-... https://pharo.fogbugz.com/f/cases/15472/Fraction-rounding-to-n-decimal-place... https://pharo.fogbugz.com/f/cases/15473/Float-round-to-n-decimal-places-is-n...
I hate to say that, but try in python, they got it right...
Le 25/10/16 à 11:05, Henrik Johansen a écrit :
+1.
Unless you're dealing with fixed precision* entities, it's usually better to specify digits to display in printing methods themselves (#printShowingDecimalPlaces: & friends in base image). As per previous discussions around this that arise every second year or so, rounding the number itself (as long as we're dealing with floats) will never work as you want reliably.
Cheers, Henry
* And in that case, you'd use ScaledDecimals
On 23 Oct 2016, at 7:08 , phil@highoctane.be <mailto:phil@highoctane.be> wrote:
I use the Printf package for that.
v := 65.456. 'With 2 decimal digits: %5.2f, or 3 like this: %6.3f' printf: {v. v}.
With 2 decimal digits: 65.45, or 3 like this: 65.456
It is in http://www.smalltalkhub.com/#!/~philippeback/HOExtras <http://www.smalltalkhub.com/#%21/%7Ephilippeback/HOExtras>
Printf
I am just used to C printf and well, I like the way it works.
Phil
2016-10-26 9:14 GMT+02:00 stepharo <stepharo@free.fr>:
Hi nicolas
So what is the solution? We can integrate fast a solution. I would really like to see them fix in Pharo 60. I'm writing a book for newbie and this is the third time I change one chapter so may be I should stop and throw away this chapter.
1) for Fraction: round: numberOfWishedDecimal v := 10 raisedTo: numberOfWishedDecimal. ^ ((self * v) rounded / v) asFloat or just replace asFloat if you wish to remain exact: round: numberOfWishedDecimal v := 10 raisedTo: numberOfWishedDecimal. ^ ((self * v) rounded / v) asScaledDecimal: numberOfWishedDecimal 2) for Float, it is in 15471: round: numberOfWishedDecimal | v maxNumberOfDecimals | maxNumberOfDecimals := self class precision - 1 - (self exponent max: self class emin). maxNumberOfDecimals < numberOfWishedDecimal ifTrue: [^self]. v := 10 raisedTo: numberOfWishedDecimal. ^ ((self asFraction * v) rounded / v) asFloat or if Fraction already answers a Float: round: numberOfWishedDecimal | maxNumberOfDecimals | maxNumberOfDecimals := self class precision - 1 - (self exponent max: self class emin). maxNumberOfDecimals < numberOfWishedDecimal ifTrue: [^self]. ^ self asFraction round: numberOfWishedDecimal It's slower than current implementation, but will round exactly to the nearest Float. It's possible to have faster implementation up to 22 decimals if you provide a fused-multiply-accumulate primitive... You see I'm not good in math so even if I try hard I will not have a good
solution. This is the same for marcus and most people in our team. I reopened these issues.
Stef
2016-10-25 23:22 GMT+02:00 stepharo <stepharo@free.fr>:
Ok for the advice
Now round: aNumberOfDigits seems to work fine.
Stef
No it's not, but issues are timing out too fast ;) https://pharo.fogbugz.com/f/cases/15471/Can-t-round-Float- fmax-to-2-decimal-places https://pharo.fogbugz.com/f/cases/15472/Fraction-rounding- to-n-decimal-places-is-inexact https://pharo.fogbugz.com/f/cases/15473/Float-round-to-n- decimal-places-is-not-in-agreement-with-printShowingDecimalPlaces
I hate to say that, but try in python, they got it right...
Le 25/10/16 à 11:05, Henrik Johansen a écrit :
+1.
Unless you're dealing with fixed precision* entities, it's usually better to specify digits to display in printing methods themselves (#printShowingDecimalPlaces: & friends in base image). As per previous discussions around this that arise every second year or so, rounding the number itself (as long as we're dealing with floats) will never work as you want reliably.
Cheers, Henry
* And in that case, you'd use ScaledDecimals
On 23 Oct 2016, at 7:08 , phil@highoctane.be wrote:
I use the Printf package for that.
v := 65.456. 'With 2 decimal digits: %5.2f, or 3 like this: %6.3f' printf: {v. v}.
With 2 decimal digits: 65.45, or 3 like this: 65.456
It is in http://www.smalltalkhub.com/#!/~philippeback/HOExtras
Printf
I am just used to C printf and well, I like the way it works.
Phil
I think the right way to solve these issues is not to think about it like a Math problem. We need to write tests showing bugs and doing lots of coverage. Then, we can look into other programming languages (Java, Ruby, Python, etc), port an implementation and ensure it's correct. This is the kind of things where people have spent days to get right and efficient. The goal is not to figure how to do it better but how to port the best implementation out there. On Wed, Oct 26, 2016 at 9:58 AM, Nicolas Cellier < nicolas.cellier.aka.nice@gmail.com> wrote:
2016-10-26 9:14 GMT+02:00 stepharo <stepharo@free.fr>:
Hi nicolas
So what is the solution? We can integrate fast a solution. I would really like to see them fix in Pharo 60. I'm writing a book for newbie and this is the third time I change one chapter so may be I should stop and throw away this chapter.
1) for Fraction:
round: numberOfWishedDecimal v := 10 raisedTo: numberOfWishedDecimal. ^ ((self * v) rounded / v) asFloat
or just replace asFloat if you wish to remain exact:
round: numberOfWishedDecimal v := 10 raisedTo: numberOfWishedDecimal. ^ ((self * v) rounded / v) asScaledDecimal: numberOfWishedDecimal
2) for Float, it is in 15471:
round: numberOfWishedDecimal | v maxNumberOfDecimals | maxNumberOfDecimals := self class precision - 1 - (self exponent max: self class emin). maxNumberOfDecimals < numberOfWishedDecimal ifTrue: [^self]. v := 10 raisedTo: numberOfWishedDecimal. ^ ((self asFraction * v) rounded / v) asFloat
or if Fraction already answers a Float:
round: numberOfWishedDecimal | maxNumberOfDecimals | maxNumberOfDecimals := self class precision - 1 - (self exponent max: self class emin). maxNumberOfDecimals < numberOfWishedDecimal ifTrue: [^self]. ^ self asFraction round: numberOfWishedDecimal
It's slower than current implementation, but will round exactly to the nearest Float. It's possible to have faster implementation up to 22 decimals if you provide a fused-multiply-accumulate primitive...
You see I'm not good in math so even if I try hard I will not have a good
solution. This is the same for marcus and most people in our team. I reopened these issues.
Stef
2016-10-25 23:22 GMT+02:00 stepharo <stepharo@free.fr>:
Ok for the advice
Now round: aNumberOfDigits seems to work fine.
Stef
No it's not, but issues are timing out too fast ;) https://pharo.fogbugz.com/f/cases/15471/Can-t-round-Float-fm ax-to-2-decimal-places https://pharo.fogbugz.com/f/cases/15472/Fraction-rounding-to -n-decimal-places-is-inexact https://pharo.fogbugz.com/f/cases/15473/Float-round-to-n-dec imal-places-is-not-in-agreement-with-printShowingDecimalPlaces
I hate to say that, but try in python, they got it right...
Le 25/10/16 à 11:05, Henrik Johansen a écrit :
+1.
Unless you're dealing with fixed precision* entities, it's usually better to specify digits to display in printing methods themselves (#printShowingDecimalPlaces: & friends in base image). As per previous discussions around this that arise every second year or so, rounding the number itself (as long as we're dealing with floats) will never work as you want reliably.
Cheers, Henry
* And in that case, you'd use ScaledDecimals
On 23 Oct 2016, at 7:08 , phil@highoctane.be wrote:
I use the Printf package for that.
v := 65.456. 'With 2 decimal digits: %5.2f, or 3 like this: %6.3f' printf: {v. v}.
With 2 decimal digits: 65.45, or 3 like this: 65.456
It is in http://www.smalltalkhub.com/#!/~philippeback/HOExtras
Printf
I am just used to C printf and well, I like the way it works.
Phil
Hi Nicolas, regarding rounding Fractions: i use fractions if i want to get an exact result (eg for comparing the result with Float calculations). if #round: returns a Float all further calcs (with Fractions) will get contaminated, since the rest will become Floats too. Hence the "asScaledDecimal: numberOfWishedDecimal" seems better to me, but i wonder why these transformations at the end are necessary at all? just for the looks? i'd suppose every person, who knows how to use Fractions, also knows how to append a #asScaledDecimal: to a result by himself, should he want that. taking as an example financial calcs that first use 2 decimals. occasionaly the final result will be basepoints, often small ones like 0.003. with scaledDecimals the result would be (ok, look like) 0 since scaledDecimals also contaminate the calc. of course one could correct this simply with an #asScaledDecimal:3 at the end. nevertheless a first look at the zero result would surprise me for a tenth of a second. werner On 10/26/2016 09:58 AM, Nicolas Cellier wrote:
2016-10-26 9:14 GMT+02:00 stepharo <stepharo@free.fr <mailto:stepharo@free.fr>>:
Hi nicolas
So what is the solution? We can integrate fast a solution. I would really like to see them fix in Pharo 60. I'm writing a book for newbie and this is the third time I change one chapter so may be I should stop and throw away this chapter.
1) for Fraction:
round: numberOfWishedDecimal v := 10 raisedTo: numberOfWishedDecimal. ^ ((self * v) rounded / v) asFloat
or just replace asFloat if you wish to remain exact:
round: numberOfWishedDecimal v := 10 raisedTo: numberOfWishedDecimal. ^ ((self * v) rounded / v) asScaledDecimal: numberOfWishedDecimal
2) for Float, it is in 15471:
round: numberOfWishedDecimal | v maxNumberOfDecimals | maxNumberOfDecimals := self class precision - 1 - (self exponent max: self class emin). maxNumberOfDecimals < numberOfWishedDecimal ifTrue: [^self]. v := 10 raisedTo: numberOfWishedDecimal. ^ ((self asFraction * v) rounded / v) asFloat
or if Fraction already answers a Float:
round: numberOfWishedDecimal | maxNumberOfDecimals | maxNumberOfDecimals := self class precision - 1 - (self exponent max: self class emin). maxNumberOfDecimals < numberOfWishedDecimal ifTrue: [^self]. ^ self asFraction round: numberOfWishedDecimal
It's slower than current implementation, but will round exactly to the nearest Float. It's possible to have faster implementation up to 22 decimals if you provide a fused-multiply-accumulate primitive...
2016-10-26 13:11 GMT+02:00 test <wkassens@libello.com>:
Hi Nicolas, regarding rounding Fractions: i use fractions if i want to get an exact result (eg for comparing the result with Float calculations). if #round: returns a Float all further calcs (with Fractions) will get contaminated, since the rest will become Floats too. Hence the "asScaledDecimal: numberOfWishedDecimal" seems better to me, but i wonder why these transformations at the end are necessary at all? just for the looks? i'd suppose every person, who knows how to use Fractions, also knows how to append a #asScaledDecimal: to a result by himself, should he want that.
taking as an example financial calcs that first use 2 decimals. occasionaly the final result will be basepoints, often small ones like 0.003. with scaledDecimals the result would be (ok, look like) 0 since scaledDecimals also contaminate the calc. of course one could correct this simply with an #asScaledDecimal:3 at the end. nevertheless a first look at the zero result would surprise me for a tenth of a second. werner
Hi Werner, I don't know the purpose of round: at all. Most often this kind of message was used before printing probably because lack of versatile formatted print messages. In Squeak I replaced most usages of roundTo: by printShowing(Max)DecimalPlaces:. Now if it has been added in Pharo and other languages, there must be some use cases I presume. Maybe the analysis could be carried on these use cases?
Beware, converting a Fraction asScaledDecimal will NOT round. Only the printString is rounded, but the number keeps its whole precision. Example (1/3 asScaledDecimal: 1)*3 = 1.0s, not 0.9s. ScaledDecimals as they are now are just Fraction with a different printString... Not very much added value. Nicolas
On 10/26/2016 09:58 AM, Nicolas Cellier wrote:
2016-10-26 9:14 GMT+02:00 stepharo <stepharo@free.fr>:
Hi nicolas
So what is the solution? We can integrate fast a solution. I would really like to see them fix in Pharo 60. I'm writing a book for newbie and this is the third time I change one chapter so may be I should stop and throw away this chapter.
1) for Fraction:
round: numberOfWishedDecimal v := 10 raisedTo: numberOfWishedDecimal. ^ ((self * v) rounded / v) asFloat
or just replace asFloat if you wish to remain exact:
round: numberOfWishedDecimal v := 10 raisedTo: numberOfWishedDecimal. ^ ((self * v) rounded / v) asScaledDecimal: numberOfWishedDecimal
2) for Float, it is in 15471:
round: numberOfWishedDecimal | v maxNumberOfDecimals | maxNumberOfDecimals := self class precision - 1 - (self exponent max: self class emin). maxNumberOfDecimals < numberOfWishedDecimal ifTrue: [^self]. v := 10 raisedTo: numberOfWishedDecimal. ^ ((self asFraction * v) rounded / v) asFloat
or if Fraction already answers a Float:
round: numberOfWishedDecimal | maxNumberOfDecimals | maxNumberOfDecimals := self class precision - 1 - (self exponent max: self class emin). maxNumberOfDecimals < numberOfWishedDecimal ifTrue: [^self]. ^ self asFraction round: numberOfWishedDecimal
It's slower than current implementation, but will round exactly to the nearest Float. It's possible to have faster implementation up to 22 decimals if you provide a fused-multiply-accumulate primitive...
Hi Nicolas, yes, i know. suppose you calc some financial thing and one intermediate result will be in $. you have to round this Fraction-result to 2 decimals. now you use this to further calc basepoints as the final result. you have to convert this to 3 decimals. looking at this final result with 2 decimals can be be irritating for a moment, if you debug the calcs. and <g> btw i never use scaledDecimals. werner On 10/26/2016 02:06 PM, Nicolas Cellier wrote:
2016-10-26 13:11 GMT+02:00 test <wkassens@libello.com <mailto:wkassens@libello.com>>:
Hi Nicolas, regarding rounding Fractions: i use fractions if i want to get an exact result (eg for comparing the result with Float calculations). if #round: returns a Float all further calcs (with Fractions) will get contaminated, since the rest will become Floats too. Hence the "asScaledDecimal: numberOfWishedDecimal" seems better to me, but i wonder why these transformations at the end are necessary at all? just for the looks? i'd suppose every person, who knows how to use Fractions, also knows how to append a #asScaledDecimal: to a result by himself, should he want that.
taking as an example financial calcs that first use 2 decimals. occasionaly the final result will be basepoints, often small ones like 0.003. with scaledDecimals the result would be (ok, look like) 0 since scaledDecimals also contaminate the calc. of course one could correct this simply with an #asScaledDecimal:3 at the end. nevertheless a first look at the zero result would surprise me for a tenth of a second. werner
Hi Werner, I don't know the purpose of round: at all. Most often this kind of message was used before printing probably because lack of versatile formatted print messages. In Squeak I replaced most usages of roundTo: by printShowing(Max)DecimalPlaces:. Now if it has been added in Pharo and other languages, there must be some use cases I presume. Maybe the analysis could be carried on these use cases?
Beware, converting a Fraction asScaledDecimal will NOT round. Only the printString is rounded, but the number keeps its whole precision. Example (1/3 asScaledDecimal: 1)*3 = 1.0s, not 0.9s.
ScaledDecimals as they are now are just Fraction with a different printString... Not very much added value.
Nicolas
On 10/26/2016 09:58 AM, Nicolas Cellier wrote:
2016-10-26 9:14 GMT+02:00 stepharo <stepharo@free.fr <mailto:stepharo@free.fr>>:
Hi nicolas
So what is the solution? We can integrate fast a solution. I would really like to see them fix in Pharo 60. I'm writing a book for newbie and this is the third time I change one chapter so may be I should stop and throw away this chapter.
1) for Fraction:
round: numberOfWishedDecimal v := 10 raisedTo: numberOfWishedDecimal. ^ ((self * v) rounded / v) asFloat
or just replace asFloat if you wish to remain exact:
round: numberOfWishedDecimal v := 10 raisedTo: numberOfWishedDecimal. ^ ((self * v) rounded / v) asScaledDecimal: numberOfWishedDecimal
2) for Float, it is in 15471:
round: numberOfWishedDecimal | v maxNumberOfDecimals | maxNumberOfDecimals := self class precision - 1 - (self exponent max: self class emin). maxNumberOfDecimals < numberOfWishedDecimal ifTrue: [^self]. v := 10 raisedTo: numberOfWishedDecimal. ^ ((self asFraction * v) rounded / v) asFloat
or if Fraction already answers a Float:
round: numberOfWishedDecimal | maxNumberOfDecimals | maxNumberOfDecimals := self class precision - 1 - (self exponent max: self class emin). maxNumberOfDecimals < numberOfWishedDecimal ifTrue: [^self]. ^ self asFraction round: numberOfWishedDecimal
It's slower than current implementation, but will round exactly to the nearest Float. It's possible to have faster implementation up to 22 decimals if you provide a fused-multiply-accumulate primitive...
Sorry for being slow minded, yes of course financial apps is exactly the case where round: would be usefull and NEEDS to be exact. 2016-10-26 14:23 GMT+02:00 test <wkassens@libello.com>:
Hi Nicolas, yes, i know. suppose you calc some financial thing and one intermediate result will be in $. you have to round this Fraction-result to 2 decimals. now you use this to further calc basepoints as the final result. you have to convert this to 3 decimals. looking at this final result with 2 decimals can be be irritating for a moment, if you debug the calcs. and <g> btw i never use scaledDecimals. werner
On 10/26/2016 02:06 PM, Nicolas Cellier wrote:
2016-10-26 13:11 GMT+02:00 test <wkassens@libello.com>:
Hi Nicolas, regarding rounding Fractions: i use fractions if i want to get an exact result (eg for comparing the result with Float calculations). if #round: returns a Float all further calcs (with Fractions) will get contaminated, since the rest will become Floats too. Hence the "asScaledDecimal: numberOfWishedDecimal" seems better to me, but i wonder why these transformations at the end are necessary at all? just for the looks? i'd suppose every person, who knows how to use Fractions, also knows how to append a #asScaledDecimal: to a result by himself, should he want that.
taking as an example financial calcs that first use 2 decimals. occasionaly the final result will be basepoints, often small ones like 0.003. with scaledDecimals the result would be (ok, look like) 0 since scaledDecimals also contaminate the calc. of course one could correct this simply with an #asScaledDecimal:3 at the end. nevertheless a first look at the zero result would surprise me for a tenth of a second. werner
Hi Werner, I don't know the purpose of round: at all. Most often this kind of message was used before printing probably because lack of versatile formatted print messages. In Squeak I replaced most usages of roundTo: by printShowing(Max) DecimalPlaces:. Now if it has been added in Pharo and other languages, there must be some use cases I presume. Maybe the analysis could be carried on these use cases?
Beware, converting a Fraction asScaledDecimal will NOT round. Only the printString is rounded, but the number keeps its whole precision. Example (1/3 asScaledDecimal: 1)*3 = 1.0s, not 0.9s.
ScaledDecimals as they are now are just Fraction with a different printString... Not very much added value.
Nicolas
On 10/26/2016 09:58 AM, Nicolas Cellier wrote:
2016-10-26 9:14 GMT+02:00 stepharo <stepharo@free.fr>:
Hi nicolas
So what is the solution? We can integrate fast a solution. I would really like to see them fix in Pharo 60. I'm writing a book for newbie and this is the third time I change one chapter so may be I should stop and throw away this chapter.
1) for Fraction:
round: numberOfWishedDecimal v := 10 raisedTo: numberOfWishedDecimal. ^ ((self * v) rounded / v) asFloat
or just replace asFloat if you wish to remain exact:
round: numberOfWishedDecimal v := 10 raisedTo: numberOfWishedDecimal. ^ ((self * v) rounded / v) asScaledDecimal: numberOfWishedDecimal
2) for Float, it is in 15471:
round: numberOfWishedDecimal | v maxNumberOfDecimals | maxNumberOfDecimals := self class precision - 1 - (self exponent max: self class emin). maxNumberOfDecimals < numberOfWishedDecimal ifTrue: [^self]. v := 10 raisedTo: numberOfWishedDecimal. ^ ((self asFraction * v) rounded / v) asFloat
or if Fraction already answers a Float:
round: numberOfWishedDecimal | maxNumberOfDecimals | maxNumberOfDecimals := self class precision - 1 - (self exponent max: self class emin). maxNumberOfDecimals < numberOfWishedDecimal ifTrue: [^self]. ^ self asFraction round: numberOfWishedDecimal
It's slower than current implementation, but will round exactly to the nearest Float. It's possible to have faster implementation up to 22 decimals if you provide a fused-multiply-accumulate primitive...
Le 26/10/16 à 14:52, Nicolas Cellier a écrit :
Sorry for being slow minded, yes of course financial apps is exactly the case where round: would be usefull and NEEDS to be exact.
so what is the solution? I'm lost again.
Hi Nicolas, yes, i know. suppose you calc some financial thing and one intermediate result will be in $. you have to round this Fraction-result to 2 decimals. now you use this to further calc basepoints as the final result. you have to convert this to 3 decimals. looking at this final result with 2 decimals can be be irritating for a moment, if you debug the calcs. and <g> btw i never use scaledDecimals. werner
On 10/26/2016 02:06 PM, Nicolas Cellier wrote:
2016-10-26 13:11 GMT+02:00 test <wkassens@libello.com <mailto:wkassens@libello.com>>:
Hi Nicolas, regarding rounding Fractions: i use fractions if i want to get an exact result (eg for comparing the result with Float calculations). if #round: returns a Float all further calcs (with Fractions) will get contaminated, since the rest will become Floats too. Hence the "asScaledDecimal: numberOfWishedDecimal" seems better to me, but i wonder why these transformations at the end are necessary at all? just for the looks? i'd suppose every person, who knows how to use Fractions, also knows how to append a #asScaledDecimal: to a result by himself, should he want that.
taking as an example financial calcs that first use 2 decimals. occasionaly the final result will be basepoints, often small ones like 0.003. with scaledDecimals the result would be (ok, look like) 0 since scaledDecimals also contaminate the calc. of course one could correct this simply with an #asScaledDecimal:3 at the end. nevertheless a first look at the zero result would surprise me for a tenth of a second. werner
Hi Werner, I don't know the purpose of round: at all. Most often this kind of message was used before printing probably because lack of versatile formatted print messages. In Squeak I replaced most usages of roundTo: by printShowing(Max)DecimalPlaces:. Now if it has been added in Pharo and other languages, there must be some use cases I presume. Maybe the analysis could be carried on these use cases?
Beware, converting a Fraction asScaledDecimal will NOT round. Only the printString is rounded, but the number keeps its whole precision. Example (1/3 asScaledDecimal: 1)*3 = 1.0s, not 0.9s.
ScaledDecimals as they are now are just Fraction with a different printString... Not very much added value.
Nicolas
On 10/26/2016 09:58 AM, Nicolas Cellier wrote:
2016-10-26 9:14 GMT+02:00 stepharo <stepharo@free.fr <mailto:stepharo@free.fr>>:
Hi nicolas
So what is the solution? We can integrate fast a solution. I would really like to see them fix in Pharo 60. I'm writing a book for newbie and this is the third time I change one chapter so may be I should stop and throw away this chapter.
1) for Fraction:
round: numberOfWishedDecimal v := 10 raisedTo: numberOfWishedDecimal. ^ ((self * v) rounded / v) asFloat
or just replace asFloat if you wish to remain exact:
round: numberOfWishedDecimal v := 10 raisedTo: numberOfWishedDecimal. ^ ((self * v) rounded / v) asScaledDecimal: numberOfWishedDecimal
2) for Float, it is in 15471:
round: numberOfWishedDecimal | v maxNumberOfDecimals | maxNumberOfDecimals := self class precision - 1 - (self exponent max: self class emin). maxNumberOfDecimals < numberOfWishedDecimal ifTrue: [^self]. v := 10 raisedTo: numberOfWishedDecimal. ^ ((self asFraction * v) rounded / v) asFloat
or if Fraction already answers a Float:
round: numberOfWishedDecimal | maxNumberOfDecimals | maxNumberOfDecimals := self class precision - 1 - (self exponent max: self class emin). maxNumberOfDecimals < numberOfWishedDecimal ifTrue: [^self]. ^ self asFraction round: numberOfWishedDecimal
It's slower than current implementation, but will round exactly to the nearest Float. It's possible to have faster implementation up to 22 decimals if you provide a fused-multiply-accumulate primitive...
I've put a single slice in the inbox for the 3 issues because they all are related. See slice comment: For Float, implement guard to prevent overflow (15471), and use exact representation for intermediate results (asFraction) so as to avoid double rounding problems (15473) For Fraction (15472), choose an exact representation for result rather than Float. This is because such method is particularly usefull for financial/monetary applications. 2016-10-26 17:28 GMT+02:00 stepharo <stepharo@free.fr>:
Le 26/10/16 à 14:52, Nicolas Cellier a écrit :
Sorry for being slow minded, yes of course financial apps is exactly the case where round: would be usefull and NEEDS to be exact.
so what is the solution? I'm lost again.
Hi Nicolas,
yes, i know. suppose you calc some financial thing and one intermediate result will be in $. you have to round this Fraction-result to 2 decimals. now you use this to further calc basepoints as the final result. you have to convert this to 3 decimals. looking at this final result with 2 decimals can be be irritating for a moment, if you debug the calcs. and <g> btw i never use scaledDecimals. werner
On 10/26/2016 02:06 PM, Nicolas Cellier wrote:
2016-10-26 13:11 GMT+02:00 test <wkassens@libello.com>:
Hi Nicolas, regarding rounding Fractions: i use fractions if i want to get an exact result (eg for comparing the result with Float calculations). if #round: returns a Float all further calcs (with Fractions) will get contaminated, since the rest will become Floats too. Hence the "asScaledDecimal: numberOfWishedDecimal" seems better to me, but i wonder why these transformations at the end are necessary at all? just for the looks? i'd suppose every person, who knows how to use Fractions, also knows how to append a #asScaledDecimal: to a result by himself, should he want that.
taking as an example financial calcs that first use 2 decimals. occasionaly the final result will be basepoints, often small ones like 0.003. with scaledDecimals the result would be (ok, look like) 0 since scaledDecimals also contaminate the calc. of course one could correct this simply with an #asScaledDecimal:3 at the end. nevertheless a first look at the zero result would surprise me for a tenth of a second. werner
Hi Werner, I don't know the purpose of round: at all. Most often this kind of message was used before printing probably because lack of versatile formatted print messages. In Squeak I replaced most usages of roundTo: by printShowing(Max)DecimalPlaces:. Now if it has been added in Pharo and other languages, there must be some use cases I presume. Maybe the analysis could be carried on these use cases?
Beware, converting a Fraction asScaledDecimal will NOT round. Only the printString is rounded, but the number keeps its whole precision. Example (1/3 asScaledDecimal: 1)*3 = 1.0s, not 0.9s.
ScaledDecimals as they are now are just Fraction with a different printString... Not very much added value.
Nicolas
On 10/26/2016 09:58 AM, Nicolas Cellier wrote:
2016-10-26 9:14 GMT+02:00 stepharo <stepharo@free.fr>:
Hi nicolas
So what is the solution? We can integrate fast a solution. I would really like to see them fix in Pharo 60. I'm writing a book for newbie and this is the third time I change one chapter so may be I should stop and throw away this chapter.
1) for Fraction:
round: numberOfWishedDecimal v := 10 raisedTo: numberOfWishedDecimal. ^ ((self * v) rounded / v) asFloat
or just replace asFloat if you wish to remain exact:
round: numberOfWishedDecimal v := 10 raisedTo: numberOfWishedDecimal. ^ ((self * v) rounded / v) asScaledDecimal: numberOfWishedDecimal
2) for Float, it is in 15471:
round: numberOfWishedDecimal | v maxNumberOfDecimals | maxNumberOfDecimals := self class precision - 1 - (self exponent max: self class emin). maxNumberOfDecimals < numberOfWishedDecimal ifTrue: [^self]. v := 10 raisedTo: numberOfWishedDecimal. ^ ((self asFraction * v) rounded / v) asFloat
or if Fraction already answers a Float:
round: numberOfWishedDecimal | maxNumberOfDecimals | maxNumberOfDecimals := self class precision - 1 - (self exponent max: self class emin). maxNumberOfDecimals < numberOfWishedDecimal ifTrue: [^self]. ^ self asFraction round: numberOfWishedDecimal
It's slower than current implementation, but will round exactly to the nearest Float. It's possible to have faster implementation up to 22 decimals if you provide a fused-multiply-accumulate primitive...
Thanks ***A LOT*** nicolas.
I've put a single slice in the inbox for the 3 issues because they all are related. See slice comment:
For Float, implement guard to prevent overflow (15471), and use exact representation for intermediate results (asFraction) so as to avoid double rounding problems (15473)
For Fraction (15472), choose an exact representation for result rather than Float. This is because such method is particularly usefull for financial/monetary applications.
2016-10-26 17:28 GMT+02:00 stepharo <stepharo@free.fr <mailto:stepharo@free.fr>>:
Le 26/10/16 à 14:52, Nicolas Cellier a écrit :
Sorry for being slow minded, yes of course financial apps is exactly the case where round: would be usefull and NEEDS to be exact.
so what is the solution? I'm lost again.
Hi Nicolas, yes, i know. suppose you calc some financial thing and one intermediate result will be in $. you have to round this Fraction-result to 2 decimals. now you use this to further calc basepoints as the final result. you have to convert this to 3 decimals. looking at this final result with 2 decimals can be be irritating for a moment, if you debug the calcs. and <g> btw i never use scaledDecimals. werner
On 10/26/2016 02:06 PM, Nicolas Cellier wrote:
2016-10-26 13:11 GMT+02:00 test <wkassens@libello.com <mailto:wkassens@libello.com>>:
Hi Nicolas, regarding rounding Fractions: i use fractions if i want to get an exact result (eg for comparing the result with Float calculations). if #round: returns a Float all further calcs (with Fractions) will get contaminated, since the rest will become Floats too. Hence the "asScaledDecimal: numberOfWishedDecimal" seems better to me, but i wonder why these transformations at the end are necessary at all? just for the looks? i'd suppose every person, who knows how to use Fractions, also knows how to append a #asScaledDecimal: to a result by himself, should he want that.
taking as an example financial calcs that first use 2 decimals. occasionaly the final result will be basepoints, often small ones like 0.003. with scaledDecimals the result would be (ok, look like) 0 since scaledDecimals also contaminate the calc. of course one could correct this simply with an #asScaledDecimal:3 at the end. nevertheless a first look at the zero result would surprise me for a tenth of a second. werner
Hi Werner, I don't know the purpose of round: at all. Most often this kind of message was used before printing probably because lack of versatile formatted print messages. In Squeak I replaced most usages of roundTo: by printShowing(Max)DecimalPlaces:. Now if it has been added in Pharo and other languages, there must be some use cases I presume. Maybe the analysis could be carried on these use cases?
Beware, converting a Fraction asScaledDecimal will NOT round. Only the printString is rounded, but the number keeps its whole precision. Example (1/3 asScaledDecimal: 1)*3 = 1.0s, not 0.9s.
ScaledDecimals as they are now are just Fraction with a different printString... Not very much added value.
Nicolas
On 10/26/2016 09:58 AM, Nicolas Cellier wrote:
2016-10-26 9:14 GMT+02:00 stepharo <stepharo@free.fr <mailto:stepharo@free.fr>>:
Hi nicolas
So what is the solution? We can integrate fast a solution. I would really like to see them fix in Pharo 60. I'm writing a book for newbie and this is the third time I change one chapter so may be I should stop and throw away this chapter.
1) for Fraction:
round: numberOfWishedDecimal v := 10 raisedTo: numberOfWishedDecimal. ^ ((self * v) rounded / v) asFloat
or just replace asFloat if you wish to remain exact:
round: numberOfWishedDecimal v := 10 raisedTo: numberOfWishedDecimal. ^ ((self * v) rounded / v) asScaledDecimal: numberOfWishedDecimal
2) for Float, it is in 15471:
round: numberOfWishedDecimal | v maxNumberOfDecimals | maxNumberOfDecimals := self class precision - 1 - (self exponent max: self class emin). maxNumberOfDecimals < numberOfWishedDecimal ifTrue: [^self]. v := 10 raisedTo: numberOfWishedDecimal. ^ ((self asFraction * v) rounded / v) asFloat
or if Fraction already answers a Float:
round: numberOfWishedDecimal | maxNumberOfDecimals | maxNumberOfDecimals := self class precision - 1 - (self exponent max: self class emin). maxNumberOfDecimals < numberOfWishedDecimal ifTrue: [^self]. ^ self asFraction round: numberOfWishedDecimal
It's slower than current implementation, but will round exactly to the nearest Float. It's possible to have faster implementation up to 22 decimals if you provide a fused-multiply-accumulate primitive...
Hi Nicolas, regarding rounding Fractions: i use fractions if i want to get an exact result (eg for comparing the result with Float calculations). if #round: returns a Float all further calcs (with Fractions) will get contaminated, since the rest will become Floats too. Hence the "asScaledDecimal: numberOfWishedDecimal" seems better to me, but i wonder why these transformations at the end are necessary at all? just for the looks? i'd suppose every person, who knows how to use Fractions, also knows how to append a #asScaledDecimal: to a result by himself, should he want that.
taking as an example financial calcs that first use 2 decimals. occasionaly the final result will be basepoints, often small ones like 0.003. with scaledDecimals the result would be (ok, look like) 0 since scaledDecimals also contaminate the calc. of course one could correct this simply with an #asScaledDecimal:3 at the end. nevertheless a first look at the zero result would surprise me for a tenth of a second. werner
Hi Werner, I don't know the purpose of round: at all. Most often this kind of message was used before printing probably because lack of versatile formatted print messages. In Squeak I replaced most usages of roundTo: by printShowing(Max)DecimalPlaces:.
So what is the definition of printShowingMaxDecimalDigit:? I have the impression that there is a confusion between the rounding a number and the printing of his value in a given format. we can deprecate roundTo: if necessary.
Now if it has been added in Pharo and other languages, there must be some use cases I presume. You mean roundTo: no idea.
Maybe the analysis could be carried on these use cases?
Beware, converting a Fraction asScaledDecimal will NOT round. Only the printString is rounded, but the number keeps its whole precision. Example (1/3 asScaledDecimal: 1)*3 = 1.0s, not 0.9s.
ScaledDecimals as they are now are just Fraction with a different printString... Not very much added value.
Nicolas
On 10/26/2016 09:58 AM, Nicolas Cellier wrote:
2016-10-26 9:14 GMT+02:00 stepharo <stepharo@free.fr <mailto:stepharo@free.fr>>:
Hi nicolas
So what is the solution? We can integrate fast a solution. I would really like to see them fix in Pharo 60. I'm writing a book for newbie and this is the third time I change one chapter so may be I should stop and throw away this chapter.
1) for Fraction:
round: numberOfWishedDecimal v := 10 raisedTo: numberOfWishedDecimal. ^ ((self * v) rounded / v) asFloat
or just replace asFloat if you wish to remain exact:
round: numberOfWishedDecimal v := 10 raisedTo: numberOfWishedDecimal. ^ ((self * v) rounded / v) asScaledDecimal: numberOfWishedDecimal
2) for Float, it is in 15471:
round: numberOfWishedDecimal | v maxNumberOfDecimals | maxNumberOfDecimals := self class precision - 1 - (self exponent max: self class emin). maxNumberOfDecimals < numberOfWishedDecimal ifTrue: [^self]. v := 10 raisedTo: numberOfWishedDecimal. ^ ((self asFraction * v) rounded / v) asFloat
or if Fraction already answers a Float:
round: numberOfWishedDecimal | maxNumberOfDecimals | maxNumberOfDecimals := self class precision - 1 - (self exponent max: self class emin). maxNumberOfDecimals < numberOfWishedDecimal ifTrue: [^self]. ^ self asFraction round: numberOfWishedDecimal
It's slower than current implementation, but will round exactly to the nearest Float. It's possible to have faster implementation up to 22 decimals if you provide a fused-multiply-accumulate primitive...
On 10/26/2016 08:27 AM, stepharo wrote:
So what is the definition of printShowingMaxDecimalDigit:? I have the impression that there is a confusion between the rounding a number and the printing of his value in a given format.
Yes, I see quite a bit of confusion in this thread about that.
we can deprecate roundTo: if necessary.
I think roundTo: is OK. #round: is not, and should be deprecated (at least for Floats). For Floats, the idea of rounding to a specific number of decimal digits is a fantasy. Here's why: Floats cannot exactly represent most common decimal fractions. For example: 0.1 -- not representable 0.2 -- not representable 0.3 -- not representable 0.4 -- not representable 0.5 -- hey, representable! 0.6 -- not representable 0.7 -- not representable 0.8 -- not representable 0.9 -- not representable 1.0 -- representable. *Printing* a Float to a specific rounded decimal format is a sensible idea, and should be encouraged. But trying for a "rounded Float" *number* just can't be done exactly (except by converting to a Fraction). Fractions can be rounded to exact decimal fractions, so something like "myFraction roundTo: 1/100" makes sense. But the current implementation of round: on Fraction that converts to a Float just gets you the misery detailed above. On 10/26/2016 01:00 PM, Nicolas Cellier wrote:
I've put a single slice in the inbox for the 3 issues because they all are related. See slice comment:
For Float, implement guard to prevent overflow (15471), and use exact representation for intermediate results (asFraction) so as to avoid double rounding problems (15473)
The double rounding problem is not the fundamental problem, the fundamental problem is that what is desired does not exist, because Floats cannot exactly represent most decimal fractions. So this can't really fix it.
For Fraction (15472), choose an exact representation for result rather than Float. This is because such method is particularly usefull for financial/monetary applications.
Yes. Fraction or ScaledDecimal. Not Floats, unless the developer understands the needs of the financial world and the limitations of Floats very well. Regards, -Martin
On 27 Oct 2016, at 08:31, Martin McClure <martin@hand2mouse.com> wrote:
On 10/26/2016 08:27 AM, stepharo wrote:
So what is the definition of printShowingMaxDecimalDigit:? I have the impression that there is a confusion between the rounding a number and the printing of his value in a given format.
Yes, I see quite a bit of confusion in this thread about that.
we can deprecate roundTo: if necessary. I think roundTo: is OK. #round: is not, and should be deprecated (at least for Floats). For Floats, the idea of rounding to a specific number of decimal digits is a fantasy. Here's why: Floats cannot exactly represent most common decimal fractions. For example: 0.1 -- not representable
0.2 -- not representable
0.3 -- not representable
0.4 -- not representable
0.5 -- hey, representable!
0.6 -- not representable
0.7 -- not representable
0.8 -- not representable
0.9 -- not representable
1.0 -- representable. *Printing* a Float to a specific rounded decimal format is a sensible idea, and should be encouraged. But trying for a "rounded Float" *number* just can't be done exactly (except by converting to a Fraction).
YES ! Nice explanation.
Fractions can be rounded to exact decimal fractions, so something like "myFraction roundTo: 1/100" makes sense. But the current implementation of round: on Fraction that converts to a Float just gets you the misery detailed above.
On 10/26/2016 01:00 PM, Nicolas Cellier wrote:
I've put a single slice in the inbox for the 3 issues because they all are related. See slice comment:
For Float, implement guard to prevent overflow (15471), and use exact representation for intermediate results (asFraction) so as to avoid double rounding problems (15473)
The double rounding problem is not the fundamental problem, the fundamental problem is that what is desired does not exist, because Floats cannot exactly represent most decimal fractions. So this can't really fix it.
For Fraction (15472), choose an exact representation for result rather than Float. This is because such method is particularly usefull for financial/monetary applications.
Yes. Fraction or ScaledDecimal. Not Floats, unless the developer understands the needs of the financial world and the limitations of Floats very well.
Regards,
-Martin
2016-10-27 8:31 GMT+02:00 Martin McClure <martin@hand2mouse.com>:
I think roundTo: is OK. #round: is not, and should be deprecated (at least for Floats). For Floats, the idea of rounding to a specific number of decimal digits is a fantasy. Here's why: Floats cannot exactly represent most common decimal fractions. For example:
0.1 -- not representable
0.2 -- not representable
0.3 -- not representable
0.4 -- not representable
0.5 -- hey, representable!
0.6 -- not representable
0.7 -- not representable
0.8 -- not representable
0.9 -- not representable
1.0 -- representable.
*Printing* a Float to a specific rounded decimal format is a sensible idea, and should be encouraged. But trying for a "rounded Float" *number* just can't be done exactly (except by converting to a Fraction).
Fractions can be rounded to exact decimal fractions, so something like "myFraction roundTo: 1/100" makes sense. But the current implementation of round: on Fraction that converts to a Float just gets you the misery detailed above.
On 10/26/2016 01:00 PM, Nicolas Cellier wrote:
I've put a single slice in the inbox for the 3 issues because they all are related. See slice comment:
For Float, implement guard to prevent overflow (15471), and use exact representation for intermediate results (asFraction) so as to avoid double rounding problems (15473)
The double rounding problem is not the fundamental problem, the fundamental problem is that what is desired does not exist, because Floats cannot exactly represent most decimal fractions. So this can't really fix it.
Exactly. Thank's for good explanation
2016-10-27 9:51 GMT+02:00 Denis Kudriashov <dionisiydk@gmail.com>:
2016-10-27 8:31 GMT+02:00 Martin McClure <martin@hand2mouse.com>:
I think roundTo: is OK. #round: is not, and should be deprecated (at least for Floats). For Floats, the idea of rounding to a specific number of decimal digits is a fantasy. Here's why: Floats cannot exactly represent most common decimal fractions. For example:
0.1 -- not representable
0.2 -- not representable
0.3 -- not representable
0.4 -- not representable
0.5 -- hey, representable!
0.6 -- not representable
0.7 -- not representable
0.8 -- not representable
0.9 -- not representable
1.0 -- representable.
*Printing* a Float to a specific rounded decimal format is a sensible idea, and should be encouraged. But trying for a "rounded Float" *number* just can't be done exactly (except by converting to a Fraction).
Fractions can be rounded to exact decimal fractions, so something like "myFraction roundTo: 1/100" makes sense. But the current implementation of round: on Fraction that converts to a Float just gets you the misery detailed above.
On 10/26/2016 01:00 PM, Nicolas Cellier wrote:
I've put a single slice in the inbox for the 3 issues because they all are related. See slice comment:
For Float, implement guard to prevent overflow (15471), and use exact representation for intermediate results (asFraction) so as to avoid double rounding problems (15473)
The double rounding problem is not the fundamental problem, the fundamental problem is that what is desired does not exist, because Floats cannot exactly represent most decimal fractions. So this can't really fix it.
Exactly. Thank's for good explanation
Nonetheless, if user asked to round a Float, we must give him back a Float. We ain't gonna answer a ScaledDecimal because we think that it's better for him: we don't know what is better for him. And we MUST do our best to round correctly to the NEAREST Float that is 0.1e0, 0.2e0, ... 1.0e0 If user asked to round a Fraction or ScaledDecimal, then it's different. We'd better keep the exactness and use ScaledDecimal rather than convert to a Float. That's exactly these two things that the SLICE posted in inbox is doing.
On 10/27/2016 10:34 AM, Nicolas Cellier wrote:
2016-10-27 9:51 GMT+02:00 Denis Kudriashov <dionisiydk@gmail.com <mailto:dionisiydk@gmail.com>>:
2016-10-27 8:31 GMT+02:00 Martin McClure <martin@hand2mouse.com <mailto:martin@hand2mouse.com>>:
I think roundTo: is OK. #round: is not, and should be deprecated (at least for Floats). For Floats, the idea of rounding to a specific number of decimal digits is a fantasy. Here's why: Floats cannot exactly represent most common decimal fractions. For example:
0.1 -- not representable
0.2 -- not representable
0.3 -- not representable
0.4 -- not representable
0.5 -- hey, representable!
0.6 -- not representable
0.7 -- not representable
0.8 -- not representable
0.9 -- not representable
1.0 -- representable.
*Printing* a Float to a specific rounded decimal format is a sensible idea, and should be encouraged. But trying for a "rounded Float" *number* just can't be done exactly (except by converting to a Fraction).
Fractions can be rounded to exact decimal fractions, so something like "myFraction roundTo: 1/100" makes sense. But the current implementation of round: on Fraction that converts to a Float just gets you the misery detailed above.
On 10/26/2016 01:00 PM, Nicolas Cellier wrote:
I've put a single slice in the inbox for the 3 issues because they all are related. See slice comment:
For Float, implement guard to prevent overflow (15471), and use exact representation for intermediate results (asFraction) so as to avoid double rounding problems (15473)
The double rounding problem is not the fundamental problem, the fundamental problem is that what is desired does not exist, because Floats cannot exactly represent most decimal fractions. So this can't really fix it.
Exactly. Thank's for good explanation
Nonetheless, if user asked to round a Float, we must give him back a Float. We ain't gonna answer a ScaledDecimal because we think that it's better for him: we don't know what is better for him. And we MUST do our best to round correctly to the NEAREST Float that is 0.1e0, 0.2e0, ... 1.0e0
If user asked to round a Fraction or ScaledDecimal, then it's different. We'd better keep the exactness and use ScaledDecimal rather than convert to a Float.
That's exactly these two things that the SLICE posted in inbox is doing.
i completely agree (well, i would have preferred a pure Fraction result in the pure Fraction case instead of a ScaledDecimal, but so what) werner p.s. a pure Fraction result in the pure Fraction case would also insure that the rest of Number is more or less independent of the not very useful ScaledDecimal
2016-10-27 11:04 GMT+02:00 test <wkassens@libello.com>:
On 10/27/2016 10:34 AM, Nicolas Cellier wrote:
2016-10-27 9:51 GMT+02:00 Denis Kudriashov <dionisiydk@gmail.com>:
2016-10-27 8:31 GMT+02:00 Martin McClure <martin@hand2mouse.com>:
I think roundTo: is OK. #round: is not, and should be deprecated (at least for Floats). For Floats, the idea of rounding to a specific number of decimal digits is a fantasy. Here's why: Floats cannot exactly represent most common decimal fractions. For example:
0.1 -- not representable
0.2 -- not representable
0.3 -- not representable
0.4 -- not representable
0.5 -- hey, representable!
0.6 -- not representable
0.7 -- not representable
0.8 -- not representable
0.9 -- not representable
1.0 -- representable.
*Printing* a Float to a specific rounded decimal format is a sensible idea, and should be encouraged. But trying for a "rounded Float" *number* just can't be done exactly (except by converting to a Fraction).
Fractions can be rounded to exact decimal fractions, so something like "myFraction roundTo: 1/100" makes sense. But the current implementation of round: on Fraction that converts to a Float just gets you the misery detailed above.
On 10/26/2016 01:00 PM, Nicolas Cellier wrote:
I've put a single slice in the inbox for the 3 issues because they all are related. See slice comment:
For Float, implement guard to prevent overflow (15471), and use exact representation for intermediate results (asFraction) so as to avoid double rounding problems (15473)
The double rounding problem is not the fundamental problem, the fundamental problem is that what is desired does not exist, because Floats cannot exactly represent most decimal fractions. So this can't really fix it.
Exactly. Thank's for good explanation
Nonetheless, if user asked to round a Float, we must give him back a Float. We ain't gonna answer a ScaledDecimal because we think that it's better for him: we don't know what is better for him. And we MUST do our best to round correctly to the NEAREST Float that is 0.1e0, 0.2e0, ... 1.0e0
If user asked to round a Fraction or ScaledDecimal, then it's different. We'd better keep the exactness and use ScaledDecimal rather than convert to a Float.
That's exactly these two things that the SLICE posted in inbox is doing.
i completely agree (well, i would have preferred a pure Fraction result in the pure Fraction case instead of a ScaledDecimal, but so what)
werner
p.s. a pure Fraction result in the pure Fraction case would also insure that the rest of Number is more or less independent of the not very useful ScaledDecimal
+1 feel free to improve the slice.
On 10/27/2016 11:13 AM, Nicolas Cellier wrote:
feel free to improve the slice.
Hi, i never made a slice and thought <stupid grin>, i could eventually try to do that in this case, but of course i did not succeed: 1. Nicolas, i do not understand, why you put asScaledDecimal: numberOfWishedDecimal at the end of Fraction>>round:. what would not work, if one simply omitted that? 2. when i make a slice i find no possibility to save it to my pc, to check whether i made things correctly and there is no changes-button in the monticello-browser to look at the diff that slice would make. of course the seasoned programmer has no problems with that, but a simple user like me? 3. this line: "< expr: '(1/3 round: 2)' result: (33/100) >" is not accepted by the browser. i have no idea how to call this example to debug it, perhaps (33/100) is not accepted as a result because Fractions are no literals? and then what? werner p.s. regarding your slice, Nicolas, FractionTest>>testRounding needs also to be changed.
Hi, To view changes from a slice using Monticello Browser: Select the slice-package and then the Pharo60/main repository before pressing changes. (See attached picture) Best regards, Henrik From: Pharo-dev [mailto:pharo-dev-bounces@lists.pharo.org] On Behalf Of test Sent: Saturday, October 29, 2016 5:01 PM To: Pharo Development List <pharo-dev@lists.pharo.org> Subject: Re: [Pharo-dev] roundTo: strange behavior On 10/27/2016 11:13 AM, Nicolas Cellier wrote: feel free to improve the slice. Hi, i never made a slice and thought <stupid grin>, i could eventually try to do that in this case, but of course i did not succeed: 1. Nicolas, i do not understand, why you put asScaledDecimal: numberOfWishedDecimal at the end of Fraction>>round:. what would not work, if one simply omitted that? 2. when i make a slice i find no possibility to save it to my pc, to check whether i made things correctly and there is no changes-button in the monticello-browser to look at the diff that slice would make. of course the seasoned programmer has no problems with that, but a simple user like me? 3. this line: "< expr: '(1/3 round: 2)' result: (33/100) >" is not accepted by the browser. i have no idea how to call this example to debug it, perhaps (33/100) is not accepted as a result because Fractions are no literals? and then what? werner p.s. regarding your slice, Nicolas, FractionTest>>testRounding needs also to be changed.
On Sat, Oct 29, 2016 at 11:01 PM, test <wkassens@libello.com> wrote:
2. when i make a slice i find no possibility to save it to my pc,
Save it to package-cache (Henrik's snapshot), then later <Copy> it from package-cache to Pharo6Inbox. Note that <Copy> doesn't automatically request your password for Pharo6Inbox like when you <Save> direct to it. You will need to edit its repository definition to add manually add your account & password.
to check whether i made things correctly and there is no changes-button in the monticello-browser to look at the diff that slice would make.
I find the preview provided by the <Merge> button more often gives me what I want to review my Slices in a fresh Image. cheers -ben
Hi Ben, this explanation helped me most, because i <big grin> realized that i need an account with a password, which i dont have. so things have resolved themselves. thanks werner
or perhaps it worked now, i think i uploaded SLICE-Issue-15471-Cant-round-Float-fmax-to-2-decimal-places-WernerKassens.1 <http://www.smalltalkhub.com/#%21/%7EPharo/Pharo60Inbox/versions/SLICE-Issue-...> werner
Hi werner to load a slice: - select it and press load. to create a slice - + slice - then select the package that will compose the slice - save the slice in the inbox Can you retry and let me know what did not work? Stef Le 29/10/16 à 17:01, test a écrit :
On 10/27/2016 11:13 AM, Nicolas Cellier wrote:
feel free to improve the slice.
Hi, i never made a slice and thought <stupid grin>, i could eventually try to do that in this case, but of course i did not succeed: 1. Nicolas, i do not understand, why you put asScaledDecimal: numberOfWishedDecimal at the end of Fraction>>round:. what would not work, if one simply omitted that? 2. when i make a slice i find no possibility to save it to my pc, to check whether i made things correctly and there is no changes-button in the monticello-browser to look at the diff that slice would make. of course the seasoned programmer has no problems with that, but a simple user like me? 3. this line: "< expr: '(1/3 round: 2)' result: (33/100) >" is not accepted by the browser. i have no idea how to call this example to debug it, perhaps (33/100) is not accepted as a result because Fractions are no literals? and then what? werner
p.s. regarding your slice, Nicolas, FractionTest>>testRounding needs also to be changed.
Le 29/10/16 à 17:01, test a écrit :
On 10/27/2016 11:13 AM, Nicolas Cellier wrote:
feel free to improve the slice.
Hi, i never made a slice and thought <stupid grin>, i could eventually try to do that in this case, but of course i did not succeed: 1. Nicolas, i do not understand, why you put asScaledDecimal: numberOfWishedDecimal at the end of Fraction>>round:. what would not work, if one simply omitted that? 2. when i make a slice i find no possibility to save it to my pc, to check whether i made things correctly and there is no changes-button in the monticello-browser to look at the diff that slice would make. of course the seasoned programmer has no problems with that, but a simple user like me? 3. this line: "< expr: '(1/3 round: 2)' result: (33/100) >" is not accepted by the browser. i have no idea how to call this example to debug it, perhaps (33/100) is not accepted as a result because Fractions are no literals? and then what?
This is an experience that we should remove "< expr: '(1/3 round: 2)' result: (33/100) >" -> "(1/3 round: 2)' -> (33/100)"
werner
p.s. regarding your slice, Nicolas, FractionTest>>testRounding needs also to be changed.
I am also for what Martin says :). And also: I'd argue that you NEVER EVER want to use simple Floats for financial applications. In financial applications you really need precision, and Floats have hardware limitations that do not provide precision. I thought ScaledDecimal (which I never used in Pharo) was meant to provide high-precision decimal numbers to the expense of using an internal representation that is not tied to the hardware limitations. -------- Original Message --------
On 10/27/2016 10:34 AM, Nicolas Cellier wrote:
2016-10-27 9:51 GMT+02:00 Denis Kudriashov <dionisiydk@gmail.com <mailto:dionisiydk@gmail.com>>:
2016-10-27 8:31 GMT+02:00 Martin McClure <martin@hand2mouse.com <mailto:martin@hand2mouse.com>>:
I think roundTo: is OK. #round: is not, and should be deprecated (at least for Floats). For Floats, the idea of rounding to a specific number of decimal digits is a fantasy. Here's why: Floats cannot exactly represent most common decimal fractions. For example:
0.1 -- not representable
0.2 -- not representable
0.3 -- not representable
0.4 -- not representable
0.5 -- hey, representable!
0.6 -- not representable
0.7 -- not representable
0.8 -- not representable
0.9 -- not representable
1.0 -- representable.
*Printing* a Float to a specific rounded decimal format is a sensible idea, and should be encouraged. But trying for a "rounded Float" *number* just can't be done exactly (except by converting to a Fraction).
Fractions can be rounded to exact decimal fractions, so something like "myFraction roundTo: 1/100" makes sense. But the current implementation of round: on Fraction that converts to a Float just gets you the misery detailed above.
On 10/26/2016 01:00 PM, Nicolas Cellier wrote:
I've put a single slice in the inbox for the 3 issues because they all are related.
See slice comment:
For Float, implement guard to prevent overflow (15471), and use exact representation for intermediate results (asFraction) so as to avoid double rounding problems (15473)
The double rounding problem is not the fundamental problem, the fundamental problem is that what is desired does not exist, because Floats cannot exactly represent most decimal fractions. So this can't really fix it.
Exactly. Thank's for good explanation
Nonetheless, if user asked to round a Float, we must give him back a Float.
We ain't gonna answer a ScaledDecimal because we think that it's better for him: we don't know what is better for him.
And we MUST do our best to round correctly to the NEAREST Float that is 0.1e0, 0.2e0, ... 1.0e0
If user asked to round a Fraction or ScaledDecimal, then it's different.
We'd better keep the exactness and use ScaledDecimal rather than convert to a Float.
That's exactly these two things that the SLICE posted in inbox is doing.
i completely agree (well, i would have preferred a pure Fraction result in the pure Fraction case instead of a ScaledDecimal, but so what)
werner
p.s. a pure Fraction result in the pure Fraction case would also insure that the rest of Number is more or less independent of the not very useful ScaledDecimal
Yes, I was working at a large insurance company and some C code that did calculations on lots and lots of entries triggered an audit due to sizeable discrepancies in monetary terms. I turned out that the code compiled on a x86 PC didn't gave the same results as the same code compiled on the mainframe. Given enough loops and runs, the skew became too large to ignore. This led to some serious finger pointing and head scratching from de devs. They found the root cause but, like, 2 months after they started looking. Also there is the banker's rounding. https://www.eecis.udel.edu/~breech/contest.inet.fall.07/problems/bankers-rou... Phil On Thu, Oct 27, 2016 at 11:15 AM, Guille Polito <guillermopolito@gmail.com> wrote:
I am also for what Martin says :).
And also: I'd argue that you NEVER EVER want to use simple Floats for financial applications. In financial applications you really need precision, and Floats have hardware limitations that do not provide precision.
I thought ScaledDecimal (which I never used in Pharo) was meant to provide high-precision decimal numbers to the expense of using an internal representation that is not tied to the hardware limitations.
-------- Original Message --------
On 10/27/2016 10:34 AM, Nicolas Cellier wrote:
2016-10-27 9:51 GMT+02:00 Denis Kudriashov <dionisiydk@gmail.com>:
2016-10-27 8:31 GMT+02:00 Martin McClure <martin@hand2mouse.com>:
I think roundTo: is OK. #round: is not, and should be deprecated (at least for Floats). For Floats, the idea of rounding to a specific number of decimal digits is a fantasy. Here's why: Floats cannot exactly represent most common decimal fractions. For example:
0.1 -- not representable
0.2 -- not representable
0.3 -- not representable
0.4 -- not representable
0.5 -- hey, representable!
0.6 -- not representable
0.7 -- not representable
0.8 -- not representable
0.9 -- not representable
1.0 -- representable.
*Printing* a Float to a specific rounded decimal format is a sensible idea, and should be encouraged. But trying for a "rounded Float" *number* just can't be done exactly (except by converting to a Fraction).
Fractions can be rounded to exact decimal fractions, so something like "myFraction roundTo: 1/100" makes sense. But the current implementation of round: on Fraction that converts to a Float just gets you the misery detailed above.
On 10/26/2016 01:00 PM, Nicolas Cellier wrote:
I've put a single slice in the inbox for the 3 issues because they all are related.
See slice comment:
For Float, implement guard to prevent overflow (15471), and use exact representation for intermediate results (asFraction) so as to avoid double rounding problems (15473)
The double rounding problem is not the fundamental problem, the fundamental problem is that what is desired does not exist, because Floats cannot exactly represent most decimal fractions. So this can't really fix it.
Exactly. Thank's for good explanation
Nonetheless, if user asked to round a Float, we must give him back a Float.
We ain't gonna answer a ScaledDecimal because we think that it's better for him: we don't know what is better for him.
And we MUST do our best to round correctly to the NEAREST Float that is 0.1e0, 0.2e0, ... 1.0e0
If user asked to round a Fraction or ScaledDecimal, then it's different.
We'd better keep the exactness and use ScaledDecimal rather than convert to a Float.
That's exactly these two things that the SLICE posted in inbox is doing.
i completely agree (well, i would have preferred a pure Fraction result in the pure Fraction case instead of a ScaledDecimal, but so what)
werner
p.s. a pure Fraction result in the pure Fraction case would also insure that the rest of Number is more or less independent of the not very useful ScaledDecimal
On 10/27/2016 01:53 PM, phil@highoctane.be wrote:
Yes, I was working at a large insurance company and some C code that did calculations on lots and lots of entries triggered an audit due to sizeable discrepancies in monetary terms.
[...]
Also there is the banker's rounding. https://www.eecis.udel.edu/~breech/contest.inet.fall.07/problems/bankers-rou... <https://www.eecis.udel.edu/%7Ebreech/contest.inet.fall.07/problems/bankers-r...>
Yes! When we were re-doing our ScaledDecimal implementation a few years ago, I moved GemStone to Banker's Rounding (round-to-even). It's fairly simple to implement, and is important to avoid biases in complex computerized calculations. I suspect that the "standard" rounding rule (if the digit is 5, round up) is a leftover from manual arithmetic -- if you're doing, say, long division, and you get a five, and the remainder is non-zero, you know that the quotient is actually greater than that, so you round up. And if you *don't* know what those digits beyond the 5 are, you round up anyway, because the only time you don't is if they're *all* zeroes. But with computer math it's easy to figure out whether all the digits will be zeroes or not, so we can use the better rounding rule. Regards, -Martin
Hi Guille, On 10/27/2016 11:15 AM, Guille Polito wrote:
I am also for what Martin says .
i would prefer if #round: will not be deprecated in Floats. in some cases Float>>#round: can be useful, eg optimization problems where one searched value describes the size of a screw, that exists only in certain sizes.
And also: I'd argue that you NEVER EVER want to use simple Floats for financial applications. In financial applications you really need precision, and Floats have hardware limitations that do not provide precision.
i'd be careful with such a bold statement. for example think of calculating future values of an asset, based on probabilities. probabilities are inexact anyway. werner
On 10/27/2016 04:12 AM, test wrote:
i would prefer if #round: will not be deprecated in Floats. in some cases Float>>#round: can be useful, eg optimization problems where one searched value describes the size of a screw, that exists only in certain sizes.
#roundTo: can be used for this (and is specified by ANSI Smalltalk, for those who care). If the screws only come in 16th inch increments, for instance, then "roundTo: 0.0625" will give you an exact result, since 16ths *are* exactly representable as floats. OTOH, #round: (not in ANSI) appears to promise something that cannot be delivered for Floats, so is misleading, and it spreads the kind of confusion that resulted in this thread. That's why I favor deprecating #round: for Floats. Regards, -Martin
Hi Martin, of course you are right, but i find it irritating and difficult to remember, if i can calculate something with one kind of number and not with next one, especially Floats & Fractions. imo they should behave similarly, that makes them easier to use for simple minds like me, for example if i compare results with Floats & Fractions. werner On 10/27/2016 06:12 PM, Martin McClure wrote:
On 10/27/2016 04:12 AM, test wrote:
i would prefer if #round: will not be deprecated in Floats. in some cases Float>>#round: can be useful, eg optimization problems where one searched value describes the size of a screw, that exists only in certain sizes.
#roundTo: can be used for this (and is specified by ANSI Smalltalk, for those who care). If the screws only come in 16th inch increments, for instance, then "roundTo: 0.0625" will give you an exact result, since 16ths *are* exactly representable as floats.
OTOH, #round: (not in ANSI) appears to promise something that cannot be delivered for Floats, so is misleading, and it spreads the kind of confusion that resulted in this thread.
That's why I favor deprecating #round: for Floats.
Regards,
-Martin
I'm favor to deprecate round: too. Thanks you all for your analysis. I will reread the thread carefully to learn. Stef Le 27/10/16 à 18:12, Martin McClure a écrit :
On 10/27/2016 04:12 AM, test wrote:
i would prefer if #round: will not be deprecated in Floats. in some cases Float>>#round: can be useful, eg optimization problems where one searched value describes the size of a screw, that exists only in certain sizes.
#roundTo: can be used for this (and is specified by ANSI Smalltalk, for those who care). If the screws only come in 16th inch increments, for instance, then "roundTo: 0.0625" will give you an exact result, since 16ths *are* exactly representable as floats.
OTOH, #round: (not in ANSI) appears to promise something that cannot be delivered for Floats, so is misleading, and it spreads the kind of confusion that resulted in this thread.
That's why I favor deprecating #round: for Floats.
Regards,
-Martin
On 10/30/2016 09:45 AM, stepharo wrote:
I'm favor to deprecate round: too.
Thanks you all for your analysis. I will reread the thread carefully to learn.
Hi Stephane, just to sum up my arguments: 1. Floats & Fractions represent almost the same thing, hence they should have the same API! iow if Float>>round: is deprecated it should imo also be deprecated in Fractions. and i dont see what is gained by generally deprecating every #round: (eg i like to use it). if it is because you want to have a small image, i'd guess, one could put ScaledDecimal in its own package (as long as there are no dependencies of numbers on it introduced) and have an image without that. the memory gain would be bigger & imo the usability loss smaller. 2. most decimals are not representable as Floats. Ok, so what? pi is not representable too, you dont deprecate Float>>pi because of that, right? werner
Hi, just a simple numerical example using nicolas' implementation: 1.199999999999999999 round: 1. "1.2" i'd guess this result is more or less what a beginner would expect with this line. how would he do it with roundTo:? perhaps this way? 1.199999999999999999 roundTo: 0.1. "1.2000000000000002" i'm not so sure that he would immediately see that he'd need to do it this way: (1.199999999999999999 roundTo: (1/10))asFloat. "1.2" werner
tl;dr: 1.2000000000000002 *is* the correct answer for 1.19 roundTo: 0.1. (1.19 roundTo: (1/10))asFloat gives a different answer because it's rounding to a multiple of exactly 0.1, not the Float nearest to 0.1. On 10/31/2016 08:35 AM, test wrote:
oops, make that original number 1.19 in the example.
OK, so the examples are now 1.19 round: 1. 1.19 roundTo: 0.1. (1.19 roundTo: (1/10))asFloat I should note that the final example *should* result in a Float without having to have #asFloat sent to it. Per ANSI and traditional Smalltalk practice, any operation where the receiver *or* the argument is a Float produces a Float. Since 0.1 is not quite equal to 1/10, the correct answer may not be the same for the latter two examples. Whether you define #round: to be one or the other is an interesting question. I've learned the hard way not to trust the results from Smalltalk math libraries too far, so let's manually figure out what the correct answer is for each of these, and see if Pharo's doing it right. To start, every Float (ignoring infinities, NaNs, and subnormals) is (by definition) a Fraction constrained to have a numerator in the range [2**52..2**53) and a denominator that is a power of two. Pharo says: 0.1 asFraction ==> (3602879701896397/36028797018963968) Is this correct? The numerator is less than 2**52, so this Fraction has been reduced. Let's restore it to what it would be in its Float form by multiplying numerator and denominator by, in this case, 2: 7205759403792794 ----------------- 72057594037927936 It's easy to see that this fraction is very nearly equal to 1/10, and to see that there could be no Float closer to 1/10. --- But I haven't shown the conversion from the string '0.1' to the actual Float instance is correct. It's possible that there's some rounding error there, which is reversed by a rounding in a different direction by the #asFraction. The Float for '0.1' is two words with values, 1069128089 and 2576980378. Convert to hex and combine them, you get 16r3FB999999999999A The numerator of our fraction is the low-order 52 bits of this, with a 1 added on the most-significant end, so 16r1999999999999A, which in decimal is 7205759403792794. Which is what we got above, so we're good so far. ======== Doing the same analysis on 1.19 gives a numerator of 16r130A3D70A3D70A, and a denominator 1/16th the previous one, or 5359283556570890 ---------------- 4503599627370496 This one's harder, I can't just look at it and say it's the closest possible to 1.19. But we know that 1.19 = 119/100. 119/100 * 4503599627370496/4503599627370496 = 535928355657089024 ------------------ 450359962737049600 and is still exactly 1.19. But to make a representable Float we have to divide numerator and denominator by 100, rounding the numerator if necessary. And we can see that the numerator is correctly rounded, so Pharo also converts the string '1.19' into a Float correctly. How does Pharo do on 1.19 asFraction? (2679641778285445/2251799813685248) Once again, this has been reduced by dividing numerator and denominator by two, but it's numerically correct. ====== Now that we know that we have the correct Fractions for the floats, we can check the rounding functions. 1.19 roundTo: 0.1 should ideally be equivalent to (1.19 asFraction roundTo: 0.1 asFraction) asFloat. In Pharo 5, 1.19 asFraction roundTo: 0.1 asFraction ==> (10808639105689191/9007199254740992) For this to correct, (10808639105689191/9007199254740992) must be a multiple of (3602879701896397/36028797018963968), since that's 0.1 asFraction. Is it a multiple? We can align the denominators by multiplying (10808639105689191/9007199254740992) by 4/4, and we get 43234556422756764 ----------------- 36028797018963968 which would be a multiple of 3602879701896397 ----------------- 36028797018963968 iff 43234556422756764 is a multiple of 3602879701896397. Looking at the original problem (1.19 roundTo: 0.1) we'd expect the multiple to be 12. Sure enough, 3602879701896397 * 12 ==> 43234556422756764. So we're correct so far. Now we have to convert our answer to the nearest representable Float. Our denominator is already a power of two, but our numerator is out of range -- it's too large. It's even, so we can cut it in half, getting 21617278211378382 Still too large, still even. Cut it in half again: 10808639105689191 Just a bit too large. Cut it in half again: 5404319552844595.5 Now it's in range [2**52..2**53) but it's not an integer, so we must round. Our number is exactly halfway between two representable numbers, so by IEEE754 we round to the even one, which is 5404319552844596 The Float we get back from 1.19 roundTo: 0.1 is 16r3FF3333333333334 and the numerator part of that is 16r13333333333334, or 5404319552844596 Which agrees with what we computed before. So this *is* the correct Float result. And it prints as 1.2000000000000002. This print string implies that there is a Float nearer to 1.2 than this. 1.2 is represented as 16r3FF3333333333333, so one representable Float less than the result of the #roundTo:. But, as we've shown, 1.2000000000000002 is *correct*. (Which was not what I expected when started this -- I suspected there might be rounding error in the computation of #roundTo:. But for this example, there isn't). I haven't done the full analysis of it, but I expect that (1.19 roundTo: (1/10))asFloat gives the Float with representation 16r3FF3333333333333 because it's rounding to a multiple of exactly 0.1, not the Float nearest to 0.1. Regards, -Martin
On Oct 31, 2016, at 3:58 PM, Martin McClure <martin@hand2mouse.com> wrote:
(1.19 roundTo: (1/10))asFloat
I should note that the final example *should* result in a Float without having to have #asFloat sent to it. Per ANSI and traditional Smalltalk practice, any operation where the receiver *or* the argument is a Float produces a Float.
While ANSI says that the numbers should be converted using the default conversion table, all of the Smalltalkâs that Iâve tried donât follow that. Iâve tried Dolphin, VW, Pharo, & VAST, and all return fractions. I prefer this behavior over the ANSI behavior. John Brant
On 10/31/2016 02:49 PM, John Brant wrote:
On Oct 31, 2016, at 3:58 PM, Martin McClure <martin@hand2mouse.com> wrote:
(1.19 roundTo: (1/10))asFloat
I should note that the final example *should* result in a Float without having to have #asFloat sent to it. Per ANSI and traditional Smalltalk practice, any operation where the receiver *or* the argument is a Float produces a Float.
While ANSI says that the numbers should be converted using the default conversion table, all of the Smalltalkâs that Iâve tried donât follow that. Iâve tried Dolphin, VW, Pharo, & VAST, and all return fractions. I prefer this behavior over the ANSI behavior.
Thanks for the information. GemStone answers a Float. Since Fractions are more general than Floats, there is indeed a good basis for arguing that this is one of the things that ANSI got wrong. Regards, -Martin
2016-10-31 22:49 GMT+01:00 John Brant <brant@refactoryworkers.com>:
On Oct 31, 2016, at 3:58 PM, Martin McClure <martin@hand2mouse.com> wrote:
(1.19 roundTo: (1/10))asFloat
I should note that the final example *should* result in a Float without having to have #asFloat sent to it. Per ANSI and traditional Smalltalk practice, any operation where the receiver *or* the argument is a Float produces a Float.
While ANSI says that the numbers should be converted using the default conversion table, all of the Smalltalkâs that Iâve tried donât follow that. Iâve tried Dolphin, VW, Pharo, & VAST, and all return fractions. I prefer this behavior over the ANSI behavior.
John Brant
Whatever result species, don't trust (aFloat roundTo: aFraction) too much, they have some chances to be inexact, except for trivial powers of two. Example: (1.105 roundTo: 1/100) -> (111/100) 1.105 < (1105/1000) -> true Though the number was smaller than exact tie (in decimal), it was rounded up. Indeed, the exact computations have a different opinion: (1.105 asFraction roundTo: 1/100) -> (11/10) That's why we must implement round: with exact computations...
Hi Martin, thanks for this analysis, i really appreciate it. you did proof that, as you said "1.2000000000000002 *is* the correct answer for 1.19 roundTo: 0.1.". please excuse me - i seriously think it was very friendly of you to do this detailed analysis -, but my argument never was, that if i use Float>roundTo: this produces problematic floating-point-errors. if it produces floating-point-errors they wouldnt be unexpected. i thought this would be clear from my previous posts. what i tried to say is, that i am a simple user and pharo shouldnt expect from me, that i know, that if i want a result looking a bit more like 1.2, i should use "(x asFraction roundTo:(1/10))asFloat" instead of "x roundTo:0.1". the #round: method circumvents the irritating part of Float calcs imo in an inexpensive way, and rounding numbers is imo not a very exotic task, but to presume that i figure out (x asFraction roundTo:(1/10))asFloat by myself is simply expecting too much from me. otoh now i that i know how to do it, it's not a problem for me, if the majority wants to deprecate #roundTo:. Thank you again for the careful analysis werner On 10/31/2016 09:58 PM, Martin McClure wrote:
tl;dr:
1.2000000000000002 *is* the correct answer for 1.19 roundTo: 0.1. (1.19 roundTo: (1/10))asFloat gives a different answer because it's rounding to a multiple of exactly 0.1, not the Float nearest to 0.1.
On 10/31/2016 08:35 AM, test wrote:
oops, make that original number 1.19 in the example. OK, so the examples are now
1.19 round: 1.
1.19 roundTo: 0.1.
(1.19 roundTo: (1/10))asFloat
I should note that the final example *should* result in a Float without having to have #asFloat sent to it. Per ANSI and traditional Smalltalk practice, any operation where the receiver *or* the argument is a Float produces a Float.
Since 0.1 is not quite equal to 1/10, the correct answer may not be the same for the latter two examples. Whether you define #round: to be one or the other is an interesting question.
I've learned the hard way not to trust the results from Smalltalk math libraries too far, so let's manually figure out what the correct answer is for each of these, and see if Pharo's doing it right.
To start, every Float (ignoring infinities, NaNs, and subnormals) is (by definition) a Fraction constrained to have a numerator in the range [2**52..2**53) and a denominator that is a power of two.
Pharo says: 0.1 asFraction ==> (3602879701896397/36028797018963968)
Is this correct? The numerator is less than 2**52, so this Fraction has been reduced. Let's restore it to what it would be in its Float form by multiplying numerator and denominator by, in this case, 2:
7205759403792794 ----------------- 72057594037927936
It's easy to see that this fraction is very nearly equal to 1/10, and to see that there could be no Float closer to 1/10.
---
But I haven't shown the conversion from the string '0.1' to the actual Float instance is correct. It's possible that there's some rounding error there, which is reversed by a rounding in a different direction by the #asFraction.
The Float for '0.1' is two words with values, 1069128089 and 2576980378. Convert to hex and combine them, you get 16r3FB999999999999A
The numerator of our fraction is the low-order 52 bits of this, with a 1 added on the most-significant end, so 16r1999999999999A, which in decimal is 7205759403792794. Which is what we got above, so we're good so far.
========
Doing the same analysis on 1.19 gives a numerator of 16r130A3D70A3D70A, and a denominator 1/16th the previous one, or
5359283556570890 ---------------- 4503599627370496
This one's harder, I can't just look at it and say it's the closest possible to 1.19.
But we know that 1.19 = 119/100.
119/100 * 4503599627370496/4503599627370496 =
535928355657089024 ------------------ 450359962737049600
and is still exactly 1.19. But to make a representable Float we have to divide numerator and denominator by 100, rounding the numerator if necessary. And we can see that the numerator is correctly rounded, so Pharo also converts the string '1.19' into a Float correctly.
How does Pharo do on 1.19 asFraction?
(2679641778285445/2251799813685248)
Once again, this has been reduced by dividing numerator and denominator by two, but it's numerically correct.
======
Now that we know that we have the correct Fractions for the floats, we can check the rounding functions.
1.19 roundTo: 0.1 should ideally be equivalent to (1.19 asFraction roundTo: 0.1 asFraction) asFloat.
In Pharo 5, 1.19 asFraction roundTo: 0.1 asFraction ==> (10808639105689191/9007199254740992)
For this to correct, (10808639105689191/9007199254740992) must be a multiple of (3602879701896397/36028797018963968), since that's 0.1 asFraction.
Is it a multiple? We can align the denominators by multiplying (10808639105689191/9007199254740992) by 4/4, and we get
43234556422756764 ----------------- 36028797018963968
which would be a multiple of
3602879701896397 ----------------- 36028797018963968
iff 43234556422756764 is a multiple of 3602879701896397. Looking at the original problem (1.19 roundTo: 0.1) we'd expect the multiple to be 12.
Sure enough, 3602879701896397 * 12 ==> 43234556422756764. So we're correct so far.
Now we have to convert our answer to the nearest representable Float.
Our denominator is already a power of two, but our numerator is out of range -- it's too large.
It's even, so we can cut it in half, getting
21617278211378382
Still too large, still even. Cut it in half again:
10808639105689191
Just a bit too large. Cut it in half again:
5404319552844595.5
Now it's in range [2**52..2**53) but it's not an integer, so we must round. Our number is exactly halfway between two representable numbers, so by IEEE754 we round to the even one, which is
5404319552844596
The Float we get back from
1.19 roundTo: 0.1
is 16r3FF3333333333334
and the numerator part of that is 16r13333333333334, or
5404319552844596
Which agrees with what we computed before. So this *is* the correct Float result. And it prints as 1.2000000000000002.
This print string implies that there is a Float nearer to 1.2 than this.
1.2 is represented as 16r3FF3333333333333, so one representable Float less than the result of the #roundTo:. But, as we've shown, 1.2000000000000002 is *correct*. (Which was not what I expected when started this -- I suspected there might be rounding error in the computation of #roundTo:. But for this example, there isn't).
I haven't done the full analysis of it, but I expect that (1.19 roundTo: (1/10))asFloat gives the Float with representation 16r3FF3333333333333 because it's rounding to a multiple of exactly 0.1, not the Float nearest to 0.1.
Regards,
-Martin
On 11/01/2016 03:28 PM, test wrote:
Hi Martin, thanks for this analysis, i really appreciate it. you did proof that, as you said "1.2000000000000002 *is* the correct answer for 1.19 roundTo: 0.1.". please excuse me - i seriously think it was very friendly of you to do this detailed analysis -, but my argument never was, that if i use Float>roundTo: this produces problematic floating-point-errors. if it produces floating-point-errors they wouldnt be unexpected. i thought this would be clear from my previous posts. what i tried to say is, that i am a simple user and pharo shouldnt expect from me, that i know, that if i want a result looking a bit more like 1.2, i should use "(x asFraction roundTo:(1/10))asFloat" instead of "x roundTo:0.1". the #round: method circumvents the irritating part of Float calcs imo in an inexpensive way, and rounding numbers is imo not a very exotic task, but to presume that i figure out (x asFraction roundTo:(1/10))asFloat by myself is simply expecting too much from me. otoh now i that i know how to do it, it's not a problem for me, if the majority wants to deprecate #roundTo:. oops ... deprecate #round: ... of course Thank you again for the careful analysis werner On 10/31/2016 09:58 PM, Martin McClure wrote:
tl;dr:
1.2000000000000002 *is* the correct answer for 1.19 roundTo: 0.1. (1.19 roundTo: (1/10))asFloat gives a different answer because it's rounding to a multiple of exactly 0.1, not the Float nearest to 0.1.
On 10/31/2016 08:35 AM, test wrote:
oops, make that original number 1.19 in the example. OK, so the examples are now
1.19 round: 1.
1.19 roundTo: 0.1.
(1.19 roundTo: (1/10))asFloat
I should note that the final example *should* result in a Float without having to have #asFloat sent to it. Per ANSI and traditional Smalltalk practice, any operation where the receiver *or* the argument is a Float produces a Float.
Since 0.1 is not quite equal to 1/10, the correct answer may not be the same for the latter two examples. Whether you define #round: to be one or the other is an interesting question.
I've learned the hard way not to trust the results from Smalltalk math libraries too far, so let's manually figure out what the correct answer is for each of these, and see if Pharo's doing it right.
To start, every Float (ignoring infinities, NaNs, and subnormals) is (by definition) a Fraction constrained to have a numerator in the range [2**52..2**53) and a denominator that is a power of two.
Pharo says: 0.1 asFraction ==> (3602879701896397/36028797018963968)
Is this correct? The numerator is less than 2**52, so this Fraction has been reduced. Let's restore it to what it would be in its Float form by multiplying numerator and denominator by, in this case, 2:
7205759403792794 ----------------- 72057594037927936
It's easy to see that this fraction is very nearly equal to 1/10, and to see that there could be no Float closer to 1/10.
---
But I haven't shown the conversion from the string '0.1' to the actual Float instance is correct. It's possible that there's some rounding error there, which is reversed by a rounding in a different direction by the #asFraction.
The Float for '0.1' is two words with values, 1069128089 and 2576980378. Convert to hex and combine them, you get 16r3FB999999999999A
The numerator of our fraction is the low-order 52 bits of this, with a 1 added on the most-significant end, so 16r1999999999999A, which in decimal is 7205759403792794. Which is what we got above, so we're good so far.
========
Doing the same analysis on 1.19 gives a numerator of 16r130A3D70A3D70A, and a denominator 1/16th the previous one, or
5359283556570890 ---------------- 4503599627370496
This one's harder, I can't just look at it and say it's the closest possible to 1.19.
But we know that 1.19 = 119/100.
119/100 * 4503599627370496/4503599627370496 =
535928355657089024 ------------------ 450359962737049600
and is still exactly 1.19. But to make a representable Float we have to divide numerator and denominator by 100, rounding the numerator if necessary. And we can see that the numerator is correctly rounded, so Pharo also converts the string '1.19' into a Float correctly.
How does Pharo do on 1.19 asFraction?
(2679641778285445/2251799813685248)
Once again, this has been reduced by dividing numerator and denominator by two, but it's numerically correct.
======
Now that we know that we have the correct Fractions for the floats, we can check the rounding functions.
1.19 roundTo: 0.1 should ideally be equivalent to (1.19 asFraction roundTo: 0.1 asFraction) asFloat.
In Pharo 5, 1.19 asFraction roundTo: 0.1 asFraction ==> (10808639105689191/9007199254740992)
For this to correct, (10808639105689191/9007199254740992) must be a multiple of (3602879701896397/36028797018963968), since that's 0.1 asFraction.
Is it a multiple? We can align the denominators by multiplying (10808639105689191/9007199254740992) by 4/4, and we get
43234556422756764 ----------------- 36028797018963968
which would be a multiple of
3602879701896397 ----------------- 36028797018963968
iff 43234556422756764 is a multiple of 3602879701896397. Looking at the original problem (1.19 roundTo: 0.1) we'd expect the multiple to be 12.
Sure enough, 3602879701896397 * 12 ==> 43234556422756764. So we're correct so far.
Now we have to convert our answer to the nearest representable Float.
Our denominator is already a power of two, but our numerator is out of range -- it's too large.
It's even, so we can cut it in half, getting
21617278211378382
Still too large, still even. Cut it in half again:
10808639105689191
Just a bit too large. Cut it in half again:
5404319552844595.5
Now it's in range [2**52..2**53) but it's not an integer, so we must round. Our number is exactly halfway between two representable numbers, so by IEEE754 we round to the even one, which is
5404319552844596
The Float we get back from
1.19 roundTo: 0.1
is 16r3FF3333333333334
and the numerator part of that is 16r13333333333334, or
5404319552844596
Which agrees with what we computed before. So this *is* the correct Float result. And it prints as 1.2000000000000002.
This print string implies that there is a Float nearer to 1.2 than this.
1.2 is represented as 16r3FF3333333333333, so one representable Float less than the result of the #roundTo:. But, as we've shown, 1.2000000000000002 is *correct*. (Which was not what I expected when started this -- I suspected there might be rounding error in the computation of #roundTo:. But for this example, there isn't).
I haven't done the full analysis of it, but I expect that (1.19 roundTo: (1/10))asFloat gives the Float with representation 16r3FF3333333333333 because it's rounding to a multiple of exactly 0.1, not the Float nearest to 0.1.
Regards,
-Martin
On 11/01/2016 07:28 AM, test wrote:
Hi Martin, thanks for this analysis, i really appreciate it. you did proof that, as you said "1.2000000000000002 *is* the correct answer for 1.19 roundTo: 0.1.". please excuse me - i seriously think it was very friendly of you to do this detailed analysis -, but my argument never was, that if i use Float>roundTo: this produces problematic floating-point-errors. if it produces floating-point-errors they wouldnt be unexpected. i thought this would be clear from my previous posts. what i tried to say is, that i am a simple user and pharo shouldnt expect from me, that i know, that if i want a result looking a bit more like 1.2, i should use "(x asFraction roundTo:(1/10))asFloat" instead of "x roundTo:0.1". the #round: method circumvents the irritating part of Float calcs imo in an inexpensive way, and rounding numbers is imo not a very exotic task, but to presume that i figure out (x asFraction roundTo:(1/10))asFloat by myself is simply expecting too much from me. otoh now i that i know how to do it, it's not a problem for me, if the majority wants to deprecate #roundTo:. Thank you again for the careful analysis werner
Hi Werner, Thanks for your comments. I posted the analysis because I did the analysis (and thought some others might want to see it), and I did the analysis because I wanted to find out whether that answer was right. Some Smalltalks are pretty bad in similar areas of Float handling. But aside from all the fine points, if you want a floating-point number to "look" nice and human-readable, (x asFraction roundTo:(1/10))asFloat will work, but I still recommend not rounding the number itself, but rounding the printing of the number. This is not a Pharo thing, it's an any-language-with-floats thing. In C you have printf, etc. In Pharo, you can use for instance: 1.19 printShowingDecimalPlaces: 1 ==> '1.2' This makes it easier for someone reading the code to see the intent. Regards, -Martin
yes Martin, i get that point and i already reacted to it, i occasionally want to calculate something with a rounded float, not print it: apart from my harley i normally use _metric screws which come in decimal steps. <friendly grin> let's end that discussion, we are going around in circles. werner On 11/02/2016 02:48 AM, Martin McClure wrote:
Hi Werner, Thanks for your comments. I posted the analysis because I did the analysis (and thought some others might want to see it), and I did the analysis because I wanted to find out whether that answer was right. Some Smalltalks are pretty bad in similar areas of Float handling.
But aside from all the fine points, if you want a floating-point number to "look" nice and human-readable, (x asFraction roundTo:(1/10))asFloat will work, but I still recommend not rounding the number itself, but rounding the printing of the number. This is not a Pharo thing, it's an any-language-with-floats thing. In C you have printf, etc. In Pharo, you can use for instance:
1.19 printShowingDecimalPlaces: 1 ==> '1.2'
This makes it easier for someone reading the code to see the intent.
Regards,
-Martin
Last thing, I'm not sure it's a good idea to deprecate round: round: has been added to overcome the rounding problems of roundTo: If we deprecate round:, then users will fallback to roundTo: and won't get correctly rounded Floats... Thus my original question: why is there a round in python, ruby etc... There must be other applications than just printing (we said financial is one). 2016-11-02 14:40 GMT+01:00 werner kassens <wkassens@libello.com>:
yes Martin, i get that point and i already reacted to it, i occasionally want to calculate something with a rounded float, not print it: apart from my harley i normally use _metric screws which come in decimal steps. <friendly grin> let's end that discussion, we are going around in circles. werner
On 11/02/2016 02:48 AM, Martin McClure wrote:
Hi Werner, Thanks for your comments. I posted the analysis because I did the analysis (and thought some others might want to see it), and I did the analysis because I wanted to find out whether that answer was right. Some Smalltalks are pretty bad in similar areas of Float handling.
But aside from all the fine points, if you want a floating-point number to "look" nice and human-readable, (x asFraction roundTo:(1/10))asFloat will work, but I still recommend not rounding the number itself, but rounding the printing of the number. This is not a Pharo thing, it's an any-language-with-floats thing. In C you have printf, etc. In Pharo, you can use for instance:
1.19 printShowingDecimalPlaces: 1 ==> '1.2'
This makes it easier for someone reading the code to see the intent.
Regards,
-Martin
Ok at least we should change the comment to point to printShowingDecimalPlaces: 1.19 printShowingDecimalPlaces: 1 STf Le 2/11/16 à 17:31, Nicolas Cellier a écrit :
Last thing, I'm not sure it's a good idea to deprecate round: round: has been added to overcome the rounding problems of roundTo: If we deprecate round:, then users will fallback to roundTo: and won't get correctly rounded Floats...
Thus my original question: why is there a round in python, ruby etc... There must be other applications than just printing (we said financial is one).
2016-11-02 14:40 GMT+01:00 werner kassens <wkassens@libello.com <mailto:wkassens@libello.com>>:
yes Martin, i get that point and i already reacted to it, i occasionally want to calculate something with a rounded float, not print it: apart from my harley i normally use _metric screws which come in decimal steps. <friendly grin> let's end that discussion, we are going around in circles. werner
On 11/02/2016 02:48 AM, Martin McClure wrote:
Hi Werner, Thanks for your comments. I posted the analysis because I did the analysis (and thought some others might want to see it), and I did the analysis because I wanted to find out whether that answer was right. Some Smalltalks are pretty bad in similar areas of Float handling.
But aside from all the fine points, if you want a floating-point number to "look" nice and human-readable, (x asFraction roundTo:(1/10))asFloat will work, but I still recommend not rounding the number itself, but rounding the printing of the number. This is not a Pharo thing, it's an any-language-with-floats thing. In C you have printf, etc. In Pharo, you can use for instance:
1.19 printShowingDecimalPlaces: 1 ==> '1.2'
This makes it easier for someone reading the code to see the intent.
Regards,
-Martin
On Mon, Oct 31, 2016 at 11:10 PM, test <wkassens@libello.com> wrote:
Hi, just a simple numerical example using nicolas' implementation: 1.199999999999999999 round: 1. "1.2" i'd guess this result is more or less what a beginner would expect with this line. how would he do it with roundTo:? perhaps this way? 1.199999999999999999 roundTo: 0.1. "1.2000000000000002" i'm not so sure that he would immediately see that he'd need to do it this way: (1.199999999999999999 roundTo: (1/10))asFloat. "1.2" werner
On Tue, Nov 1, 2016 at 4:58 AM, Martin McClure <martin@hand2mouse.com> wrote:
tl;dr:
1.2000000000000002 *is* the correct answer for 1.19 roundTo: 0.1. (1.19 roundTo: (1/10))asFloat gives a different answer because it's rounding to a multiple of exactly 0.1, not the Float nearest to 0.1.
So then why don't we *really* help the naive programmer make "x roundTo: 0.1" an error? Conceptually... Number>>roundTo: quantum quantum < 1 ifTrue: [ quantum isFraction ifFalse: [ SubtleArithmeticError signal ]. ^(self / quantum) rounded * quantum SubtleArithmeticError >> defaultAction self inform: 'Decimals have an inexact Float representation, so rounding may not give you what you expect. Us Fractions instead.'. cheers -ben
On 10/31/2016 05:07 PM, Ben Coman wrote:
So then why don't we *really* help the naive programmer make "x roundTo: 0.1" an error? Conceptually...
Number>>roundTo: quantum quantum < 1 ifTrue: [ quantum isFraction ifFalse: [ SubtleArithmeticError signal ]. ^(self / quantum) rounded * quantum
SubtleArithmeticError >> defaultAction self inform: 'Decimals have an inexact Float representation, so rounding may not give you what you expect. Us Fractions instead.'.
cheers -ben
While this is tempting, this philosophy would, I'm afraid, lead to signaling this error on *all* uses of Floats, because Floats are *full* of this kind of behavior that is surprising to the uninitiated. Regards, -Martin
On 10/27/2016 01:34 AM, Nicolas Cellier wrote:
Nonetheless, if user asked to round a Float, we must give him back a Float. We ain't gonna answer a ScaledDecimal because we think that it's better for him: we don't know what is better for him.
Right, the result of #round: should always be the same kind of number as the receiver. Regards, -Martin
Hey Martin... I agree on the confusion.
I still recommend not rounding the number itself, but rounding the printing of the number
Yes. If "rounding" had well specified behavior that was at least somewhat reasonable (e.g. return the closest floating point value and round to even if tied), I'd accept floating point results that don't print "pretty" in base 10. That's the way things are, there's no point in pretending IEEE-754 is not base 2. If "rounding" is being done in place of "printing", or obtaining a "string representation", I think that won't work as expected because 0.1 not representable etc. For "printing", the algorithms were Dragon4 and Grisu3 last time I checked. For "floating point that works nice in base 10", there's IEEE-854. For those unfamiliar with the spec, it's been implemented in non-exotic hardware already.
you did proof that, as you said "1.2000000000000002 *is* the correct answer for 1.19 roundTo: 0.1.".
As a suggestion for these types of problems, try iterating over a floating point bit range converting each to a true fraction and see which one is closest?
testIfCompletelyBroken [doesn't round to even]
I'd agree rounding to even would be better. Guillermo,
And also: I'd argue that you NEVER EVER want to use simple Floats for financial applications
Define "financial" applications? In the risk management world, tying down that last $1 difference will likely cost more electricity (and way more time) than the $1 is worth. John,
While ANSI says that the numbers should be converted using the default conversion table, all of the Smalltalkâs that Iâve tried donât follow that. Iâve tried Dolphin, VW, Pharo, & VAST, and all return fractions. I prefer this behavior over the ANSI behavior.
I agree that ANSI doesn't always make the best sense. If it were up to me, I'd follow the relevant standard to the letter. That's more important than providing a custom interpretation of an interoperability mechanism just for Smalltalk (or a particular dialect) by default. Nicolas,
printShowingDecimalPlaces:
That's quite a long selector. Has anyone considered an approach along the lines of this? 1.2345 %f #'3.2' => ' 1.23' 1.2345 %f #'03.2' => '001.23' A reminder: calling printf() via FFI is undefined behavior. Andres. On 10/26/16 23:31 , Martin McClure wrote:
On 10/26/2016 08:27 AM, stepharo wrote:
So what is the definition of printShowingMaxDecimalDigit:? I have the impression that there is a confusion between the rounding a number and the printing of his value in a given format.
Yes, I see quite a bit of confusion in this thread about that.
we can deprecate roundTo: if necessary.
I think roundTo: is OK. #round: is not, and should be deprecated (at least for Floats). For Floats, the idea of rounding to a specific number of decimal digits is a fantasy. Here's why: Floats cannot exactly represent most common decimal fractions. For example:
0.1 -- not representable
0.2 -- not representable
0.3 -- not representable
0.4 -- not representable
0.5 -- hey, representable!
0.6 -- not representable
0.7 -- not representable
0.8 -- not representable
0.9 -- not representable
1.0 -- representable.
*Printing* a Float to a specific rounded decimal format is a sensible idea, and should be encouraged. But trying for a "rounded Float" *number* just can't be done exactly (except by converting to a Fraction).
Fractions can be rounded to exact decimal fractions, so something like "myFraction roundTo: 1/100" makes sense. But the current implementation of round: on Fraction that converts to a Float just gets you the misery detailed above.
On 10/26/2016 01:00 PM, Nicolas Cellier wrote:
I've put a single slice in the inbox for the 3 issues because they all are related. See slice comment:
For Float, implement guard to prevent overflow (15471), and use exact representation for intermediate results (asFraction) so as to avoid double rounding problems (15473)
The double rounding problem is not the fundamental problem, the fundamental problem is that what is desired does not exist, because Floats cannot exactly represent most decimal fractions. So this can't really fix it.
For Fraction (15472), choose an exact representation for result rather than Float. This is because such method is particularly usefull for financial/monetary applications.
Yes. Fraction or ScaledDecimal. Not Floats, unless the developer understands the needs of the financial world and the limitations of Floats very well.
Regards,
-Martin
On 11/25/2016 08:33 PM, Andres Valloud wrote:
If "rounding" had well specified behavior that was at least somewhat reasonable (e.g. return the closest floating point value and round to even if tied), I'd accept floating point results that don't print "pretty" in base 10. That's the way things are, there's no point in pretending IEEE-754 is not base 2.
If "rounding" is being done in place of "printing", or obtaining a "string representation", I think that won't work as expected because 0.1 not representable etc.
For "printing", the algorithms were Dragon4 and Grisu3 last time I checked.
Thanks for the reference! Grisu3 looks interesting.
For "floating point that works nice in base 10", there's IEEE-854. For those unfamiliar with the spec, it's been implemented in non-exotic hardware already.
Base 2 and base 10 are now both covered in IEEE-754 as of the 2008 revision (which also ties up some loose ends that were not specified adequately in IEEE 754-1985) Regards, -Martin
Hi guys I loaded the slice 15471 of werner and I run https://pharo.fogbugz.com/f/cases/15471/Can-t-round-Float-fmax-to-2-decimal-... testIfCompletelyBroken "If the results are not these values (accounting for precision of printing) then something is horribly wrong" gen seed: 2345678901. self assert: (((1 to: 10) collect: [:i | gen next round: 15]) = #(0.149243269650845 0.331633021743797 0.75619644800024 0.393701540023881 0.941783181364547 0.549929193942775 0.659962596213428 0.991354559078512 0.696074432551896 0.922987899707159 )). I get the following. Is it correct? #(0.220388752510952 0.073763451573329 0.74233059293699 0.350275491993071 0.080193927548916 0.819340314631974 0.652668019594936 0.391405332084468 0.349416343657959 0.640487859323848)
Hi Stephan, definitely no. after the test is run, the seed has changed, perhaps you did not reset the seed. ill look into this this night, have to work now, ok? werner On 11/04/2016 03:29 PM, stepharo wrote:
Hi guys
I loaded the slice 15471 of werner and I run
https://pharo.fogbugz.com/f/cases/15471/Can-t-round-Float-fmax-to-2-decimal-...
testIfCompletelyBroken
"If the results are not these values (accounting for precision of printing) then something is horribly wrong"
gen seed: 2345678901. self assert: (((1 to: 10) collect: [:i | gen next round: 15]) = #(0.149243269650845 0.331633021743797 0.75619644800024 0.393701540023881 0.941783181364547 0.549929193942775 0.659962596213428 0.991354559078512 0.696074432551896 0.922987899707159 )).
I get the following. Is it correct?
#(0.220388752510952 0.073763451573329 0.74233059293699 0.350275491993071 0.080193927548916 0.819340314631974 0.652668019594936 0.391405332084468 0.349416343657959 0.640487859323848)
Le 4/11/16 à 17:03, werner kassens a écrit :
Hi Stephan, definitely no. after the test is run, the seed has changed, perhaps you did not reset the seed. I just run the test. Nothing else.
ill look into this this night, have to work now, ok? tx!
werner
On 11/04/2016 03:29 PM, stepharo wrote:
Hi guys
I loaded the slice 15471 of werner and I run
https://pharo.fogbugz.com/f/cases/15471/Can-t-round-Float-fmax-to-2-decimal-...
testIfCompletelyBroken
"If the results are not these values (accounting for precision of printing) then something is horribly wrong"
gen seed: 2345678901. self assert: (((1 to: 10) collect: [:i | gen next round: 15]) = #(0.149243269650845 0.331633021743797 0.75619644800024 0.393701540023881 0.941783181364547 0.549929193942775 0.659962596213428 0.991354559078512 0.696074432551896 0.922987899707159 )).
I get the following. Is it correct?
#(0.220388752510952 0.073763451573329 0.74233059293699 0.350275491993071 0.080193927548916 0.819340314631974 0.652668019594936 0.391405332084468 0.349416343657959 0.640487859323848)
Hi Stephane, i uploaded a SLICE-Issue-15471-Cant-round-Float-fmax-to-2-decimal-places-WernerKassens.2 to the inbox with these changes: i corrected RandomTest>>testIfCompletelyBroken, completed tests with some border cases and the usual specifications, and added your comment to Float>>round:. the testIfCompletelyBroken needed to be changed: "original in test: #(0.149243269650845 0.331633021743797 0.75619644800024 0.393701540023881 0.941783181364547 0.549929193942775 0.659962596213428 0.991354559078512 0.696074432551896 0.922987899707159 )" gen := Random seed: 2345678901. raw:= (1 to: 10) collect: [:i | gen next] . "#(0.14924326965084453 0.3316330217437972 0.7561964480002394 0.3937015400238808 0.9417831813645471 0.5499291939427746 0.6599625962134277 0.991354559078512 0.6960744325518955 0.922987899707159)" gen seed: 2345678901. test:=(1 to: 10) collect: [:i | gen next round: 15]. "#(0.149243269650845 0.331633021743797 0.756196448000239 0.393701540023881 0.941783181364547 0.549929193942775 0.659962596213428 0.991354559078512 0.696074432551895 0.922987899707159)" "rounding here is correct, 5 and higher is rounded up, 4 and lower down" "differences to the original are the result of originally wrong rounding; compare with the raw result." werner
Hi, i noticed that i have quite some difficulties in producing a slice that makes at least a minimal sort of sense, hence a stupid question: is it ok to make a slice dependent on a preceeding slice? i would guess not, since in the meantime a lot of newer versions of a package in the slice are in Pharo60/main hence there will be regressions that have to be filtered out by the integrator (unfortunately it is relatively unclear to me how the integrator operates and how much is automated). but if it is not ok, how do i proceed if i only want to change a small part of the preceeding slice? werner
Hi, fwiw when i redid that #testIfCompletelyBroken i noticed a sort of bug in that new #round: - well, i think its a feature -, but i should probably mention it nevertheless: 1.15 round:1. "1.1" "down" 1.25 round:1. "1.3" "up" 1.35 round:1. "1.4" "up" 1.45 round:1. "1.4" "down" 1.55 round:1. "1.6" "up" 1.65 round:1. "1.6" "down" btw the same happens with #printShowingDecimalPlaces: (which is unchanged, happens eg also in 4.0). if one wants to change this to the normal behaviour, i'd guess one could do it perhaps by using #asMinimalDecimalFraction instead of #asFraction in #round: i have to admit that i have no problems at all with this idiosyncrasy and would _not use #asMinimalDecimalFraction in #round: for these reasons: 1. i see it as a poor mans round-to-even (this bankers rounding is done so that the sum of rounded numbers does not have an upward bias in relation to the sum of the original unrounded numbers). 2. although i havent tested it, i assume #asMinimalDecimalFraction is noticeably slower than #asFraction, because the former one has to use a #whileFalse: iteration. 3. round-to-even imprints a definite structure on eg random-numbers, the structure of the new #round: is at least less simple & does not produce too many even decimals, insofar it's even a rich mans round-to-even (in a way a non-random, hence repeatable, stochastic rounding). normal rounding of course introduces an even less wanted upward bias. But <stupid grin> perhaps somebody else wants to have a more predictable behaviour of #round:? werner On 11/04/2016 10:26 PM, werner kassens wrote:
Hi Stephane, i uploaded a SLICE-Issue-15471-Cant-round-Float-fmax-to-2-decimal-places-WernerKassens.2 to the inbox with these changes: i corrected RandomTest>>testIfCompletelyBroken, completed tests with some border cases and the usual specifications, and added your comment to Float>>round:.
On 11/05/2016 06:17 AM, werner kassens wrote:
Hi, fwiw when i redid that #testIfCompletelyBroken i noticed a sort of bug in that new #round: - well, i think its a feature -, but i should probably mention it nevertheless: 1.15 round:1. "1.1" "down" 1.25 round:1. "1.3" "up" 1.35 round:1. "1.4" "up" 1.45 round:1. "1.4" "down" 1.55 round:1. "1.6" "up" 1.65 round:1. "1.6" "down" btw the same happens with #printShowingDecimalPlaces: (which is unchanged, happens eg also in 4.0).
if one wants to change this to the normal behaviour, i'd guess one could do it perhaps by using #asMinimalDecimalFraction instead of #asFraction in #round:
This is, in part, an example of it being difficult to predict, at a glance, what #round: should do, without an understanding of the strengths and limitations of binary floating point numbers. Remember that Floats are not usually exact representations of nice decimal fractions. So most of these numbers are not *exactly* halfway between their possible roundings. 1.15 < (115/100) "true" 1.25 = (125/100) "true" 1.35 > (135/100) "true" 1.45 < (145/100) "true" 1.55 > (155/100) "true" 1.65 < (165/100) "true" In every case except 1.25, the answer is the correct rounding, that, is the one that is closer. For 1.25, the number *is* exactly halfway between two roundings. For round-to-even (which I recommend) the answer should be 1.2, so it gets that one "wrong". Regards, -Martin
Hi Martin, yes, what i said was wrong (!). i got irritated when i used #printShowingDecimalPlaces: on a directly entered decimal number <g> and got a seemingly upwards rounded number. of course that result was also correct. iow there is no problem in the new #round: or #printShowingDecimalPlaces:. sorry for my confusion. werner On 11/06/2016 01:57 AM, Martin McClure wrote:
On 11/05/2016 06:17 AM, werner kassens wrote:
Hi, fwiw when i redid that #testIfCompletelyBroken i noticed a sort of bug in that new #round: - well, i think its a feature -, but i should probably mention it nevertheless: 1.15 round:1. "1.1" "down" 1.25 round:1. "1.3" "up" 1.35 round:1. "1.4" "up" 1.45 round:1. "1.4" "down" 1.55 round:1. "1.6" "up" 1.65 round:1. "1.6" "down" btw the same happens with #printShowingDecimalPlaces: (which is unchanged, happens eg also in 4.0).
if one wants to change this to the normal behaviour, i'd guess one could do it perhaps by using #asMinimalDecimalFraction instead of #asFraction in #round:
This is, in part, an example of it being difficult to predict, at a glance, what #round: should do, without an understanding of the strengths and limitations of binary floating point numbers.
Remember that Floats are not usually exact representations of nice decimal fractions. So most of these numbers are not *exactly* halfway between their possible roundings.
1.15 < (115/100) "true" 1.25 = (125/100) "true" 1.35 > (135/100) "true" 1.45 < (145/100) "true" 1.55 > (155/100) "true" 1.65 < (165/100) "true"
In every case except 1.25, the answer is the correct rounding, that, is the one that is closer. For 1.25, the number *is* exactly halfway between two roundings. For round-to-even (which I recommend) the answer should be 1.2, so it gets that one "wrong".
Regards,
-Martin
participants (18)
-
Andres Valloud -
Ben Coman -
Clément Bera -
Denis Kudriashov -
Dimitris Chloupis -
Guille Polito -
Henrik Johansen -
Henrik Nergaard -
John Brant -
Martin McClure -
Nicolas Cellier -
Peter Uhnak -
phil@highoctane.be -
stepharo -
Sven Van Caekenberghe -
test -
Thierry Goubier -
werner kassens